bots: Rename BotHandler to AbstractBotHandler.

Fixes #690.
This commit is contained in:
Niloth P 2024-11-20 18:46:08 +05:30 committed by Tim Abbott
parent fb73220438
commit 1d37ed2217
42 changed files with 155 additions and 143 deletions

View file

@ -2,7 +2,7 @@
from typing import Any, Dict from typing import Any, Dict
import packaged_helloworld import packaged_helloworld
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
__version__ = packaged_helloworld.__version__ __version__ = packaged_helloworld.__version__
@ -17,7 +17,7 @@ class HelloWorldHandler:
sophisticated, bots that can be installed separately. sophisticated, bots that can be installed separately.
""" """
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
content = "beep boop" content = "beep boop"
bot_handler.send_reply(message, content) bot_handler.send_reply(message, content)

View file

@ -4,11 +4,11 @@ from typing import Any, Dict, List
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class BaremetricsHandler: class BaremetricsHandler:
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("baremetrics") self.config_info = bot_handler.get_config_info("baremetrics")
self.api_key = self.config_info["api_key"] self.api_key = self.config_info["api_key"]
@ -38,7 +38,7 @@ class BaremetricsHandler:
self.check_api_key(bot_handler) self.check_api_key(bot_handler)
def check_api_key(self, bot_handler: BotHandler) -> None: def check_api_key(self, bot_handler: AbstractBotHandler) -> None:
url = "https://api.baremetrics.com/v1/account" url = "https://api.baremetrics.com/v1/account"
test_query_response = requests.get(url, headers=self.auth_header) test_query_response = requests.get(url, headers=self.auth_header)
test_query_data = test_query_response.json() test_query_data = test_query_response.json()
@ -57,7 +57,7 @@ class BaremetricsHandler:
Version 1.0 Version 1.0
""" """
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
content = message["content"].strip().split() content = message["content"].strip().split()
if content == []: if content == []:

View file

@ -4,7 +4,7 @@ from typing import Dict
import requests import requests
from requests.exceptions import ConnectionError from requests.exceptions import ConnectionError
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
help_message = """ help_message = """
You can add datapoints towards your beeminder goals \ You can add datapoints towards your beeminder goals \
@ -80,7 +80,7 @@ class BeeminderHandler:
towards their beeminder goals via zulip towards their beeminder goals via zulip
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("beeminder") self.config_info = bot_handler.get_config_info("beeminder")
# Check for valid auth_token # Check for valid auth_token
auth_token = self.config_info["auth_token"] auth_token = self.config_info["auth_token"]
@ -96,7 +96,7 @@ class BeeminderHandler:
def usage(self) -> str: def usage(self) -> str:
return "This plugin allows users to add datapoints towards their Beeminder goals" return "This plugin allows users to add datapoints towards their Beeminder goals"
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
response = get_beeminder_response(message["content"], self.config_info) response = get_beeminder_response(message["content"], self.config_info)
bot_handler.send_reply(message, response) bot_handler.send_reply(message, response)

View file

@ -5,7 +5,7 @@ from typing import Dict, Optional
import chess import chess
import chess.engine import chess.engine
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
START_REGEX = re.compile("start with other user$") START_REGEX = re.compile("start with other user$")
START_COMPUTER_REGEX = re.compile("start as (?P<user_color>white|black) with computer") START_COMPUTER_REGEX = re.compile("start as (?P<user_color>white|black) with computer")
@ -24,7 +24,7 @@ class ChessHandler:
"Stockfish program on this computer." "Stockfish program on this computer."
) )
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("chess") self.config_info = bot_handler.get_config_info("chess")
try: try:
@ -36,7 +36,7 @@ class ChessHandler:
# runner is testing or knows they won't be using an engine. # runner is testing or knows they won't be using an engine.
print("That Stockfish doesn't exist. Continuing.") print("That Stockfish doesn't exist. Continuing.")
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
content = message["content"] content = message["content"]
if content == "": if content == "":
@ -76,7 +76,7 @@ class ChessHandler:
elif resign_regex_match: elif resign_regex_match:
self.resign(message, bot_handler, last_fen) self.resign(message, bot_handler, last_fen)
def start(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def start(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
"""Starts a game with another user, with the current user as white. """Starts a game with another user, with the current user as white.
Replies to the bot handler. Replies to the bot handler.
@ -93,7 +93,7 @@ class ChessHandler:
bot_handler.storage.put("last_fen", new_board.fen()) bot_handler.storage.put("last_fen", new_board.fen())
def start_computer( def start_computer(
self, message: Dict[str, str], bot_handler: BotHandler, is_white_user: bool self, message: Dict[str, str], bot_handler: AbstractBotHandler, is_white_user: bool
) -> None: ) -> None:
"""Starts a game with the computer. Replies to the bot handler. """Starts a game with the computer. Replies to the bot handler.
@ -123,7 +123,7 @@ class ChessHandler:
) )
def validate_board( def validate_board(
self, message: Dict[str, str], bot_handler: BotHandler, fen: str self, message: Dict[str, str], bot_handler: AbstractBotHandler, fen: str
) -> Optional[chess.Board]: ) -> Optional[chess.Board]:
"""Validates a board based on its FEN string. Replies to the bot """Validates a board based on its FEN string. Replies to the bot
handler if there is an error with the board. handler if there is an error with the board.
@ -147,7 +147,7 @@ class ChessHandler:
def validate_move( def validate_move(
self, self,
message: Dict[str, str], message: Dict[str, str],
bot_handler: BotHandler, bot_handler: AbstractBotHandler,
last_board: chess.Board, last_board: chess.Board,
move_san: str, move_san: str,
is_computer: object, is_computer: object,
@ -180,7 +180,7 @@ class ChessHandler:
return move return move
def check_game_over( def check_game_over(
self, message: Dict[str, str], bot_handler: BotHandler, new_board: chess.Board self, message: Dict[str, str], bot_handler: AbstractBotHandler, new_board: chess.Board
) -> bool: ) -> bool:
"""Checks if a game is over due to """Checks if a game is over due to
- checkmate, - checkmate,
@ -224,7 +224,7 @@ class ChessHandler:
return False return False
def move( def move(
self, message: Dict[str, str], bot_handler: BotHandler, last_fen: str, move_san: str self, message: Dict[str, str], bot_handler: AbstractBotHandler, last_fen: str, move_san: str
) -> None: ) -> None:
"""Makes a move for a user in a game with another user. Replies to """Makes a move for a user in a game with another user. Replies to
the bot handler. the bot handler.
@ -256,7 +256,7 @@ class ChessHandler:
bot_handler.storage.put("last_fen", new_board.fen()) bot_handler.storage.put("last_fen", new_board.fen())
def move_computer( def move_computer(
self, message: Dict[str, str], bot_handler: BotHandler, last_fen: str, move_san: str self, message: Dict[str, str], bot_handler: AbstractBotHandler, last_fen: str, move_san: str
) -> None: ) -> None:
"""Preforms a move for a user in a game with the computer and then """Preforms a move for a user in a game with the computer and then
makes the computer's move. Replies to the bot handler. Unlike `move`, makes the computer's move. Replies to the bot handler. Unlike `move`,
@ -306,7 +306,7 @@ class ChessHandler:
bot_handler.storage.put("last_fen", new_board_after_computer_move.fen()) bot_handler.storage.put("last_fen", new_board_after_computer_move.fen())
def move_computer_first( def move_computer_first(
self, message: Dict[str, str], bot_handler: BotHandler, last_fen: str self, message: Dict[str, str], bot_handler: AbstractBotHandler, last_fen: str
) -> None: ) -> None:
"""Preforms a move for the computer without having the user go first in """Preforms a move for the computer without having the user go first in
a game with the computer. Replies to the bot handler. Like a game with the computer. Replies to the bot handler. Like
@ -345,7 +345,9 @@ class ChessHandler:
# `bot_handler`'s `storage` only accepts `str` values. # `bot_handler`'s `storage` only accepts `str` values.
bot_handler.storage.put("is_with_computer", str(True)) bot_handler.storage.put("is_with_computer", str(True))
def resign(self, message: Dict[str, str], bot_handler: BotHandler, last_fen: str) -> None: def resign(
self, message: Dict[str, str], bot_handler: AbstractBotHandler, last_fen: str
) -> None:
"""Resigns the game for the current player. """Resigns the game for the current player.
Parameters: Parameters:

View file

@ -5,7 +5,7 @@ from math import floor, log10
from typing import Any, Dict, List from typing import Any, Dict, List
from zulip_bots.bots.converter import utils from zulip_bots.bots.converter import utils
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
def is_float(value: Any) -> bool: def is_float(value: Any) -> bool:
@ -49,12 +49,12 @@ class ConverterHandler:
all supported units. all supported units.
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
bot_response = get_bot_converter_response(message, bot_handler) bot_response = get_bot_converter_response(message, bot_handler)
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)
def get_bot_converter_response(message: Dict[str, str], bot_handler: BotHandler) -> str: def get_bot_converter_response(message: Dict[str, str], bot_handler: AbstractBotHandler) -> str:
content = message["content"] content = message["content"]
words = content.lower().split() words = content.lower().split()

View file

@ -6,7 +6,7 @@ from typing import Dict
import html2text import html2text
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class DefineHandler: class DefineHandler:
@ -27,7 +27,7 @@ class DefineHandler:
messages with @mention-bot. messages with @mention-bot.
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
original_content = message["content"].strip() original_content = message["content"].strip()
bot_response = self.get_bot_define_response(original_content) bot_response = self.get_bot_define_response(original_content)

View file

@ -5,7 +5,7 @@ from typing import Dict
import apiai import apiai
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
help_message = """DialogFlow bot help_message = """DialogFlow bot
This bot will interact with dialogflow bots. This bot will interact with dialogflow bots.
@ -47,7 +47,7 @@ class DialogFlowHandler:
DialogFlow bots to zulip DialogFlow bots to zulip
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("dialogflow") self.config_info = bot_handler.get_config_info("dialogflow")
def usage(self) -> str: def usage(self) -> str:
@ -56,7 +56,7 @@ class DialogFlowHandler:
DialogFlow bots to zulip DialogFlow bots to zulip
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
result = get_bot_result(message["content"], self.config_info, message["sender_id"]) result = get_bot_result(message["content"], self.config_info, message["sender_id"])
bot_handler.send_reply(message, result) bot_handler.send_reply(message, result)

View file

@ -3,7 +3,7 @@ from typing import Any, Dict, List, Tuple
from dropbox import Dropbox from dropbox import Dropbox
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
URL = "[{name}](https://www.dropbox.com/home{path})" URL = "[{name}](https://www.dropbox.com/home{path})"
@ -14,7 +14,7 @@ class DropboxHandler:
between zulip and your dropbox account. between zulip and your dropbox account.
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("dropbox_share") self.config_info = bot_handler.get_config_info("dropbox_share")
self.ACCESS_TOKEN = self.config_info.get("access_token") self.ACCESS_TOKEN = self.config_info.get("access_token")
self.client = Dropbox(self.ACCESS_TOKEN) self.client = Dropbox(self.ACCESS_TOKEN)
@ -22,7 +22,7 @@ class DropboxHandler:
def usage(self) -> str: def usage(self) -> str:
return get_help() return get_help()
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
command = message["content"] command = message["content"]
if command == "": if command == "":
command = "help" command = "help"

View file

@ -1,6 +1,6 @@
from typing import Dict from typing import Dict
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
def encrypt(text: str) -> str: def encrypt(text: str) -> str:
@ -34,7 +34,7 @@ class EncryptHandler:
Feeding encrypted messages into the bot decrypts them. Feeding encrypted messages into the bot decrypts them.
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
bot_response = self.get_bot_encrypt_response(message) bot_response = self.get_bot_encrypt_response(message)
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)

View file

@ -2,7 +2,7 @@ import os
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Dict
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class FileUploaderHandler: class FileUploaderHandler:
@ -13,7 +13,7 @@ class FileUploaderHandler:
"\n- @uploader help : Display help message" "\n- @uploader help : Display help message"
) )
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
help_str = ( help_str = (
"Use this bot with any of the following commands:" "Use this bot with any of the following commands:"
"\n* `@uploader <local_file_path>` : Upload a file, where `<local_file_path>` is the path to the file" "\n* `@uploader <local_file_path>` : Upload a file, where `<local_file_path>` is the path to the file"

View file

@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional, Tuple
import requests import requests
from requests.exceptions import ConnectionError from requests.exceptions import ConnectionError
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
USERS_LIST_URL = "https://api.flock.co/v1/roster.listContacts" USERS_LIST_URL = "https://api.flock.co/v1/roster.listContacts"
SEND_MESSAGE_URL = "https://api.flock.co/v1/chat.sendMessage" SEND_MESSAGE_URL = "https://api.flock.co/v1/chat.sendMessage"
@ -97,14 +97,14 @@ class FlockHandler:
flock user without having to leave Zulip. flock user without having to leave Zulip.
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("flock") self.config_info = bot_handler.get_config_info("flock")
def usage(self) -> str: def usage(self) -> str:
return """Hello from Flock Bot. You can send messages to any Flock user return """Hello from Flock Bot. You can send messages to any Flock user
right from Zulip.""" right from Zulip."""
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
response = get_flock_bot_response(message["content"], self.config_info) response = get_flock_bot_response(message["content"], self.config_info)
bot_handler.send_reply(message, response) bot_handler.send_reply(message, response)

View file

@ -1,7 +1,7 @@
# See readme.md for instructions on running this code. # See readme.md for instructions on running this code.
from typing import Dict from typing import Dict
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class FollowupHandler: class FollowupHandler:
@ -26,11 +26,11 @@ class FollowupHandler:
called "followup" that your API user can send to. called "followup" that your API user can send to.
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("followup", optional=False) self.config_info = bot_handler.get_config_info("followup", optional=False)
self.stream = self.config_info.get("stream", "followup") self.stream = self.config_info.get("stream", "followup")
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
if message["content"] == "": if message["content"] == "":
bot_response = ( bot_response = (
"Please specify the message you want to send to followup stream after @mention-bot" "Please specify the message you want to send to followup stream after @mention-bot"

View file

@ -3,7 +3,7 @@ from typing import Any, Dict
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class FrontHandler: class FrontHandler:
@ -24,7 +24,7 @@ class FrontHandler:
Front Bot, `front.conf` must be set up. See `doc.md` for more details. Front Bot, `front.conf` must be set up. See `doc.md` for more details.
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
config = bot_handler.get_config_info("front") config = bot_handler.get_config_info("front")
api_key = config.get("api_key") api_key = config.get("api_key")
if not api_key: if not api_key:
@ -32,14 +32,14 @@ class FrontHandler:
self.auth = "Bearer " + api_key self.auth = "Bearer " + api_key
def help(self, bot_handler: BotHandler) -> str: def help(self, bot_handler: AbstractBotHandler) -> str:
response = "" response = ""
for command, description in self.COMMANDS: for command, description in self.COMMANDS:
response += f"`{command}` {description}\n" response += f"`{command}` {description}\n"
return response return response
def archive(self, bot_handler: BotHandler) -> str: def archive(self, bot_handler: AbstractBotHandler) -> str:
response = requests.patch( response = requests.patch(
self.FRONT_API.format(self.conversation_id), self.FRONT_API.format(self.conversation_id),
headers={"Authorization": self.auth}, headers={"Authorization": self.auth},
@ -51,7 +51,7 @@ class FrontHandler:
return "Conversation was archived." return "Conversation was archived."
def delete(self, bot_handler: BotHandler) -> str: def delete(self, bot_handler: AbstractBotHandler) -> str:
response = requests.patch( response = requests.patch(
self.FRONT_API.format(self.conversation_id), self.FRONT_API.format(self.conversation_id),
headers={"Authorization": self.auth}, headers={"Authorization": self.auth},
@ -63,7 +63,7 @@ class FrontHandler:
return "Conversation was deleted." return "Conversation was deleted."
def spam(self, bot_handler: BotHandler) -> str: def spam(self, bot_handler: AbstractBotHandler) -> str:
response = requests.patch( response = requests.patch(
self.FRONT_API.format(self.conversation_id), self.FRONT_API.format(self.conversation_id),
headers={"Authorization": self.auth}, headers={"Authorization": self.auth},
@ -75,7 +75,7 @@ class FrontHandler:
return "Conversation was marked as spam." return "Conversation was marked as spam."
def restore(self, bot_handler: BotHandler) -> str: def restore(self, bot_handler: AbstractBotHandler) -> str:
response = requests.patch( response = requests.patch(
self.FRONT_API.format(self.conversation_id), self.FRONT_API.format(self.conversation_id),
headers={"Authorization": self.auth}, headers={"Authorization": self.auth},
@ -87,7 +87,7 @@ class FrontHandler:
return "Conversation was restored." return "Conversation was restored."
def comment(self, bot_handler: BotHandler, **kwargs: Any) -> str: def comment(self, bot_handler: AbstractBotHandler, **kwargs: Any) -> str:
response = requests.post( response = requests.post(
self.FRONT_API.format(self.conversation_id) + "/comments", self.FRONT_API.format(self.conversation_id) + "/comments",
headers={"Authorization": self.auth}, headers={"Authorization": self.auth},
@ -99,7 +99,7 @@ class FrontHandler:
return "Comment was sent." return "Comment was sent."
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
command = message["content"] command = message["content"]
result = re.search(self.CNV_ID_REGEXP, message["subject"]) result = re.search(self.CNV_ID_REGEXP, message["subject"])

View file

@ -5,7 +5,7 @@ import requests
from requests.exceptions import ConnectionError, HTTPError from requests.exceptions import ConnectionError, HTTPError
from zulip_bots.custom_exceptions import ConfigValidationError from zulip_bots.custom_exceptions import ConfigValidationError
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
GIPHY_TRANSLATE_API = "http://api.giphy.com/v1/gifs/translate" GIPHY_TRANSLATE_API = "http://api.giphy.com/v1/gifs/translate"
GIPHY_RANDOM_API = "http://api.giphy.com/v1/gifs/random" GIPHY_RANDOM_API = "http://api.giphy.com/v1/gifs/random"
@ -44,10 +44,10 @@ class GiphyHandler:
) )
raise ConfigValidationError(error_message) from e raise ConfigValidationError(error_message) from e
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("giphy") self.config_info = bot_handler.get_config_info("giphy")
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
bot_response = get_bot_giphy_response(message, bot_handler, self.config_info) bot_response = get_bot_giphy_response(message, bot_handler, self.config_info)
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)
@ -82,7 +82,7 @@ def get_url_gif_giphy(keyword: str, api_key: str) -> Union[int, str]:
def get_bot_giphy_response( def get_bot_giphy_response(
message: Dict[str, str], bot_handler: BotHandler, config_info: Dict[str, str] message: Dict[str, str], bot_handler: AbstractBotHandler, config_info: Dict[str, str]
) -> str: ) -> str:
# Each exception has a specific reply should "gif_url" return a number. # Each exception has a specific reply should "gif_url" return a number.
# The bot will post the appropriate message for the error. # The bot will post the appropriate message for the error.

View file

@ -4,7 +4,7 @@ from typing import Any, Dict, Tuple, Union
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class GithubHandler: class GithubHandler:
@ -16,7 +16,7 @@ class GithubHandler:
GITHUB_ISSUE_URL_TEMPLATE = "https://api.github.com/repos/{owner}/{repo}/issues/{id}" GITHUB_ISSUE_URL_TEMPLATE = "https://api.github.com/repos/{owner}/{repo}/issues/{id}"
HANDLE_MESSAGE_REGEX = re.compile(r"(?:([\w-]+)\/)?([\w-]+)?#(\d+)") HANDLE_MESSAGE_REGEX = re.compile(r"(?:([\w-]+)\/)?([\w-]+)?#(\d+)")
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("github_detail", optional=True) self.config_info = bot_handler.get_config_info("github_detail", optional=True)
self.owner = self.config_info.get("owner", False) self.owner = self.config_info.get("owner", False)
self.repo = self.config_info.get("repo", False) self.repo = self.config_info.get("repo", False)
@ -73,7 +73,7 @@ class GithubHandler:
repo = self.repo repo = self.repo
return (owner, repo) return (owner, repo)
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
# Send help message # Send help message
if message["content"] == "help": if message["content"] == "help":
bot_handler.send_reply(message, self.usage()) bot_handler.send_reply(message, self.usage())

View file

@ -5,7 +5,7 @@ from typing import Dict, List
import requests import requests
from bs4 import BeautifulSoup, Tag from bs4 import BeautifulSoup, Tag
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
def google_search(keywords: str) -> List[Dict[str, str]]: def google_search(keywords: str) -> List[Dict[str, str]]:
@ -83,7 +83,7 @@ class GoogleSearchHandler:
@mentioned-bot. @mentioned-bot.
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
original_content = message["content"] original_content = message["content"]
result = get_google_result(original_content) result = get_google_result(original_content)
bot_handler.send_reply(message, result) bot_handler.send_reply(message, result)

View file

@ -2,7 +2,7 @@
from typing import Any, Dict from typing import Any, Dict
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class HelloWorldHandler: class HelloWorldHandler:
@ -15,7 +15,7 @@ class HelloWorldHandler:
sophisticated, bots. sophisticated, bots.
""" """
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
content = "beep boop" content = "beep boop"
bot_handler.send_reply(message, content) bot_handler.send_reply(message, content)

View file

@ -1,7 +1,7 @@
# See readme.md for instructions on running this code. # See readme.md for instructions on running this code.
from typing import Dict from typing import Dict
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class HelpHandler: class HelpHandler:
@ -15,7 +15,7 @@ class HelpHandler:
your Zulip instance. your Zulip instance.
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
help_content = "Info on Zulip can be found here:\nhttps://github.com/zulip/zulip" help_content = "Info on Zulip can be found here:\nhttps://github.com/zulip/zulip"
bot_handler.send_reply(message, help_content) bot_handler.send_reply(message, help_content)

View file

@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
API_BASE_URL = "https://beta.idonethis.com/api/v2" API_BASE_URL = "https://beta.idonethis.com/api/v2"
@ -147,7 +147,7 @@ More information in my help"""
class IDoneThisHandler: class IDoneThisHandler:
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
global api_key, default_team # noqa: PLW0603 global api_key, default_team # noqa: PLW0603
self.config_info = bot_handler.get_config_info("idonethis") self.config_info = bot_handler.get_config_info("idonethis")
if "api_key" in self.config_info: if "api_key" in self.config_info:
@ -207,7 +207,7 @@ Below are some of the commands you can use, and what they do.
+ default_team_message + default_team_message
) )
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
bot_handler.send_reply(message, self.get_response(message)) bot_handler.send_reply(message, self.get_response(message))
def get_response(self, message: Dict[str, Any]) -> str: def get_response(self, message: Dict[str, Any]) -> str:

View file

@ -2,7 +2,7 @@ import json
import re import re
from typing import Any, Dict, Tuple from typing import Any, Dict, Tuple
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
QUESTION = "How should we handle this?" QUESTION = "How should we handle this?"
@ -28,7 +28,7 @@ class IncidentHandler:
glue code here should be pretty portable. glue code here should be pretty portable.
""" """
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
query = message["content"] query = message["content"]
if query.startswith("new "): if query.startswith("new "):
start_new_incident(query, message, bot_handler) start_new_incident(query, message, bot_handler)
@ -46,7 +46,9 @@ class IncidentHandler:
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)
def start_new_incident(query: str, message: Dict[str, Any], bot_handler: BotHandler) -> None: def start_new_incident(
query: str, message: Dict[str, Any], bot_handler: AbstractBotHandler
) -> None:
# Here is where we would enter the incident in some sort of backend # Here is where we would enter the incident in some sort of backend
# system. We just simulate everything by having an incident id that # system. We just simulate everything by having an incident id that
# we generate here. # we generate here.

View file

@ -2,7 +2,7 @@
from typing import Dict, Final from typing import Dict, Final
from zulip_bots.lib import BotHandler, use_storage from zulip_bots.lib import AbstractBotHandler, use_storage
class IncrementorHandler: class IncrementorHandler:
@ -19,13 +19,13 @@ class IncrementorHandler:
is @-mentioned, this number will be incremented in the same message. is @-mentioned, this number will be incremented in the same message.
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
storage = bot_handler.storage storage = bot_handler.storage
if not storage.contains("number") or not storage.contains("message_id"): if not storage.contains("number") or not storage.contains("message_id"):
storage.put("number", 0) storage.put("number", 0)
storage.put("message_id", None) storage.put("message_id", None)
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
with use_storage(bot_handler.storage, ["number"]) as storage: with use_storage(bot_handler.storage, ["number"]) as storage:
num = storage.get("number") num = storage.get("number")

View file

@ -4,7 +4,7 @@ from typing import Any, Dict, Optional
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
GET_REGEX = re.compile('get "(?P<issue_key>.+)"$') GET_REGEX = re.compile('get "(?P<issue_key>.+)"$')
CREATE_REGEX = re.compile( CREATE_REGEX = re.compile(
@ -153,7 +153,7 @@ class JiraHandler:
Jira Bot, `jira.conf` must be set up. See `doc.md` for more details. Jira Bot, `jira.conf` must be set up. See `doc.md` for more details.
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
config = bot_handler.get_config_info("jira") config = bot_handler.get_config_info("jira")
username = config.get("username") username = config.get("username")
@ -205,7 +205,7 @@ class JiraHandler:
return response return response
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
content = message.get("content") content = message.get("content")
response = "" response = ""

View file

@ -3,7 +3,7 @@ from typing import Any, Dict
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class LinkShortenerHandler: class LinkShortenerHandler:
@ -18,11 +18,11 @@ class LinkShortenerHandler:
"`key` must be set in `link_shortener.conf`." "`key` must be set in `link_shortener.conf`."
) )
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("link_shortener") self.config_info = bot_handler.get_config_info("link_shortener")
self.check_api_key(bot_handler) self.check_api_key(bot_handler)
def check_api_key(self, bot_handler: BotHandler) -> None: def check_api_key(self, bot_handler: AbstractBotHandler) -> None:
test_request_data: Any = self.call_link_shorten_service("www.youtube.com/watch") test_request_data: Any = self.call_link_shorten_service("www.youtube.com/watch")
try: try:
if self.is_invalid_token_error(test_request_data): if self.is_invalid_token_error(test_request_data):
@ -38,7 +38,7 @@ class LinkShortenerHandler:
and response_json["status_txt"] == "INVALID_ARG_ACCESS_TOKEN" and response_json["status_txt"] == "INVALID_ARG_ACCESS_TOKEN"
) )
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
regex_str = ( regex_str = (
r"(" r"("
r"(?:http|https):\/\/" # This allows for the HTTP or HTTPS r"(?:http|https):\/\/" # This allows for the HTTP or HTTPS

View file

@ -4,18 +4,18 @@ from typing import Any, Dict, List
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class MentionHandler: class MentionHandler:
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("mention") self.config_info = bot_handler.get_config_info("mention")
self.access_token = self.config_info["access_token"] self.access_token = self.config_info["access_token"]
self.account_id = "" self.account_id = ""
self.check_access_token(bot_handler) self.check_access_token(bot_handler)
def check_access_token(self, bot_handler: BotHandler) -> None: def check_access_token(self, bot_handler: AbstractBotHandler) -> None:
test_query_header = { test_query_header = {
"Authorization": "Bearer " + self.access_token, "Authorization": "Bearer " + self.access_token,
"Accept-Version": "1.15", "Accept-Version": "1.15",
@ -43,7 +43,7 @@ class MentionHandler:
Version 1.00 Version 1.00
""" """
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
message["content"] = message["content"].strip() message["content"] = message["content"].strip()
if message["content"].lower() == "help": if message["content"].lower() == "help":

View file

@ -2,7 +2,7 @@ import logging
from typing import Dict from typing import Dict
from zulip_bots.bots.monkeytestit.lib import parse from zulip_bots.bots.monkeytestit.lib import parse
from zulip_bots.lib import BotHandler, NoBotConfigError from zulip_bots.lib import AbstractBotHandler, NoBotConfigError
class MonkeyTestitBot: class MonkeyTestitBot:
@ -17,7 +17,7 @@ class MonkeyTestitBot:
"Check doc.md for more options and setup instructions." "Check doc.md for more options and setup instructions."
) )
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
try: try:
self.config = bot_handler.get_config_info("monkeytestit") self.config = bot_handler.get_config_info("monkeytestit")
except NoBotConfigError: except NoBotConfigError:
@ -47,7 +47,7 @@ class MonkeyTestitBot:
" your api_key value and try again." " your api_key value and try again."
) )
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
content = message["content"] content = message["content"]
response = parse.execute(content, self.api_key) response = parse.execute(content, self.api_key)

View file

@ -10,7 +10,7 @@ from simple_salesforce import Salesforce # type: ignore[attr-defined]
from simple_salesforce.exceptions import SalesforceAuthenticationFailed from simple_salesforce.exceptions import SalesforceAuthenticationFailed
from zulip_bots.bots.salesforce.utils import commands, default_query, link_query, object_types from zulip_bots.bots.salesforce.utils import commands, default_query, link_query, object_types
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
base_help_text = """Salesforce bot base_help_text = """Salesforce bot
This bot can do simple salesforce query requests This bot can do simple salesforce query requests
@ -162,7 +162,7 @@ class SalesforceHandler:
return "Usage: {} [arguments]".format(command["template"]) return "Usage: {} [arguments]".format(command["template"])
return get_help_text() return get_help_text()
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("salesforce") self.config_info = bot_handler.get_config_info("salesforce")
try: try:
self.sf = Salesforce( self.sf = Salesforce(
@ -173,7 +173,7 @@ class SalesforceHandler:
except SalesforceAuthenticationFailed as err: except SalesforceAuthenticationFailed as err:
bot_handler.quit(f"Failed to log in to Salesforce. {err.code} {err.message}") bot_handler.quit(f"Failed to log in to Salesforce. {err.code} {err.message}")
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
try: try:
bot_response = self.get_salesforce_response(message["content"]) bot_response = self.get_salesforce_response(message["content"])
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)

View file

@ -3,7 +3,7 @@ from typing import Dict, Final, Optional
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
# See readme.md for instructions on running this code. # See readme.md for instructions on running this code.
@ -31,12 +31,12 @@ class StackOverflowHandler:
should preface query with "@mention-bot". should preface query with "@mention-bot".
@mention-bot <search query>""" @mention-bot <search query>"""
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
bot_response = self.get_bot_stackoverflow_response(message, bot_handler) bot_response = self.get_bot_stackoverflow_response(message, bot_handler)
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)
def get_bot_stackoverflow_response( def get_bot_stackoverflow_response(
self, message: Dict[str, str], bot_handler: BotHandler self, message: Dict[str, str], bot_handler: AbstractBotHandler
) -> Optional[str]: ) -> Optional[str]:
"""This function returns the URLs of the requested topic.""" """This function returns the URLs of the requested topic."""

View file

@ -2,7 +2,7 @@ from typing import Dict
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class SusiHandler: class SusiHandler:
@ -38,7 +38,7 @@ class SusiHandler:
``` ```
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
msg = message["content"] msg = message["content"]
if msg in ("help", ""): if msg in ("help", ""):
bot_handler.send_reply(message, self.usage()) bot_handler.send_reply(message, self.usage())

View file

@ -2,7 +2,7 @@ from typing import Any, Dict, List
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
supported_commands = [ supported_commands = [
("help", "Get the bot usage information."), ("help", "Get the bot usage information."),
@ -18,7 +18,7 @@ RESPONSE_ERROR_MESSAGE = "Invalid Response. Please check configuration and param
class TrelloHandler: class TrelloHandler:
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("trello") self.config_info = bot_handler.get_config_info("trello")
self.api_key = self.config_info["api_key"] self.api_key = self.config_info["api_key"]
self.access_token = self.config_info["access_token"] self.access_token = self.config_info["access_token"]
@ -28,7 +28,7 @@ class TrelloHandler:
self.check_access_token(bot_handler) self.check_access_token(bot_handler)
def check_access_token(self, bot_handler: BotHandler) -> None: def check_access_token(self, bot_handler: AbstractBotHandler) -> None:
test_query_response = requests.get( test_query_response = requests.get(
f"https://api.trello.com/1/members/{self.user_name}/", params=self.auth_params f"https://api.trello.com/1/members/{self.user_name}/", params=self.auth_params
) )
@ -43,7 +43,7 @@ class TrelloHandler:
Use `list-commands` to get information about the supported commands. Use `list-commands` to get information about the supported commands.
""" """
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
content = message["content"].strip().split() content = message["content"].strip().split()
if content == []: if content == []:

View file

@ -6,7 +6,7 @@ from typing import Any, Dict, Tuple
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class NotAvailableError(Exception): class NotAvailableError(Exception):
@ -23,7 +23,7 @@ class TriviaQuizHandler:
This plugin will give users a trivia question from This plugin will give users a trivia question from
the open trivia database at opentdb.com.""" the open trivia database at opentdb.com."""
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
query = message["content"] query = message["content"]
if query == "new": if query == "new":
try: try:
@ -59,11 +59,11 @@ class TriviaQuizHandler:
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)
def get_quiz_from_id(quiz_id: str, bot_handler: BotHandler) -> str: def get_quiz_from_id(quiz_id: str, bot_handler: AbstractBotHandler) -> str:
return bot_handler.storage.get(quiz_id) return bot_handler.storage.get(quiz_id)
def start_new_quiz(message: Dict[str, Any], bot_handler: BotHandler) -> None: def start_new_quiz(message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
quiz = get_trivia_quiz() quiz = get_trivia_quiz()
quiz_id = generate_quiz_id(bot_handler.storage) quiz_id = generate_quiz_id(bot_handler.storage)
bot_response = format_quiz_for_markdown(quiz_id, quiz) bot_response = format_quiz_for_markdown(quiz_id, quiz)
@ -188,7 +188,7 @@ Q: {question}
return content return content
def update_quiz(quiz: Dict[str, Any], quiz_id: str, bot_handler: BotHandler) -> None: def update_quiz(quiz: Dict[str, Any], quiz_id: str, bot_handler: AbstractBotHandler) -> None:
bot_handler.storage.put(quiz_id, json.dumps(quiz)) bot_handler.storage.put(quiz_id, json.dumps(quiz))
@ -203,7 +203,11 @@ def build_response(is_correct: bool, num_answers: int) -> str:
def handle_answer( def handle_answer(
quiz: Dict[str, Any], option: str, quiz_id: str, bot_handler: BotHandler, sender_name: str quiz: Dict[str, Any],
option: str,
quiz_id: str,
bot_handler: AbstractBotHandler,
sender_name: str,
) -> Tuple[bool, str]: ) -> Tuple[bool, str]:
answer = quiz["answers"][quiz["correct_letter"]] answer = quiz["answers"][quiz["correct_letter"]]
is_new_answer = option not in quiz["answered_options"] is_new_answer = option not in quiz["answered_options"]

View file

@ -2,7 +2,7 @@ from typing import Dict
import tweepy import tweepy
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class TwitpostBot: class TwitpostBot:
@ -21,7 +21,7 @@ class TwitpostBot:
" * @twitpost tweet hey batman\n" " * @twitpost tweet hey batman\n"
) )
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("twitter") self.config_info = bot_handler.get_config_info("twitter")
auth = tweepy.OAuthHandler( auth = tweepy.OAuthHandler(
self.config_info["consumer_key"], self.config_info["consumer_secret"] self.config_info["consumer_key"], self.config_info["consumer_secret"]
@ -31,7 +31,7 @@ class TwitpostBot:
) )
self.api = tweepy.API(auth, parser=tweepy.parsers.JSONParser()) self.api = tweepy.API(auth, parser=tweepy.parsers.JSONParser())
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
content = message["content"] content = message["content"]
if content.strip() == "": if content.strip() == "":

View file

@ -4,7 +4,7 @@ import os
import re import re
from typing import Any, Dict, Final, List, Set, Tuple, Union from typing import Any, Dict, Final, List, Set, Tuple, Union
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class VirtualFsHandler: class VirtualFsHandler:
@ -16,7 +16,7 @@ class VirtualFsHandler:
def usage(self) -> str: def usage(self) -> str:
return get_help() return get_help()
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
command = message["content"] command = message["content"]
if command == "": if command == "":
command = "help" command = "help"

View file

@ -3,18 +3,18 @@ from typing import Any, Dict
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
api_url = "http://api.openweathermap.org/data/2.5/weather" api_url = "http://api.openweathermap.org/data/2.5/weather"
class WeatherHandler: class WeatherHandler:
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.api_key = bot_handler.get_config_info("weather")["key"] self.api_key = bot_handler.get_config_info("weather")["key"]
self.response_pattern = "Weather in {}, {}:\n{:.2f} F / {:.2f} C\n{}" self.response_pattern = "Weather in {}, {}:\n{:.2f} F / {:.2f} C\n{}"
self.check_api_key(bot_handler) self.check_api_key(bot_handler)
def check_api_key(self, bot_handler: BotHandler) -> None: def check_api_key(self, bot_handler: AbstractBotHandler) -> None:
api_params = dict(q="nyc", APPID=self.api_key) api_params = dict(q="nyc", APPID=self.api_key)
test_response = requests.get(api_url, params=api_params) test_response = requests.get(api_url, params=api_params)
try: try:
@ -29,7 +29,7 @@ class WeatherHandler:
This plugin will give info about weather in a specified city This plugin will give info about weather in a specified city
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
help_content = """ help_content = """
This bot returns weather info for specified city. This bot returns weather info for specified city.
You specify city in the following format: You specify city in the following format:

View file

@ -3,7 +3,7 @@ from typing import Dict, Final
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
# See readme.md for instructions on running this code. # See readme.md for instructions on running this code.
@ -33,11 +33,13 @@ class WikipediaHandler:
should preface searches with "@mention-bot". should preface searches with "@mention-bot".
@mention-bot <name of article>""" @mention-bot <name of article>"""
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
bot_response = self.get_bot_wiki_response(message, bot_handler) bot_response = self.get_bot_wiki_response(message, bot_handler)
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)
def get_bot_wiki_response(self, message: Dict[str, str], bot_handler: BotHandler) -> str: def get_bot_wiki_response(
self, message: Dict[str, str], bot_handler: AbstractBotHandler
) -> str:
"""This function returns the URLs of the requested topic.""" """This function returns the URLs of the requested topic."""
help_text = "Please enter your search term after {}" help_text = "Please enter your search term after {}"

View file

@ -6,7 +6,7 @@ from typing import Any, Callable, Dict, Optional
import wit import wit
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class WitaiHandler: class WitaiHandler:
@ -16,7 +16,7 @@ class WitaiHandler:
Wit.ai bot, `witai.conf` must be set up. See `doc.md` for more details. Wit.ai bot, `witai.conf` must be set up. See `doc.md` for more details.
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
config = bot_handler.get_config_info("witai") config = bot_handler.get_config_info("witai")
token = config.get("token") token = config.get("token")
@ -37,7 +37,7 @@ class WitaiHandler:
self.client = wit.Wit(token) self.client = wit.Wit(token)
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
if message["content"] == "" or message["content"] == "help": if message["content"] == "" or message["content"] == "help":
bot_handler.send_reply(message, self.help_message) bot_handler.send_reply(message, self.help_message)
return return

View file

@ -4,7 +4,7 @@ from typing import Dict, Final, Optional
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
XKCD_TEMPLATE_URL = "https://xkcd.com/%s/info.0.json" XKCD_TEMPLATE_URL = "https://xkcd.com/%s/info.0.json"
LATEST_XKCD_URL = "https://xkcd.com/info.0.json" LATEST_XKCD_URL = "https://xkcd.com/info.0.json"
@ -36,7 +36,7 @@ class XkcdHandler:
`<comic_id>`, e.g `@mention-bot 1234`. `<comic_id>`, e.g `@mention-bot 1234`.
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
quoted_name = bot_handler.identity().mention quoted_name = bot_handler.identity().mention
xkcd_bot_response = get_xkcd_bot_response(message, quoted_name) xkcd_bot_response = get_xkcd_bot_response(message, quoted_name)
bot_handler.send_reply(message, xkcd_bot_response) bot_handler.send_reply(message, xkcd_bot_response)

View file

@ -5,7 +5,7 @@ from typing import Dict
import requests import requests
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
HELP_MESSAGE = """ HELP_MESSAGE = """
This bot allows users to translate a sentence into This bot allows users to translate a sentence into
@ -36,7 +36,7 @@ class YodaSpeakHandler:
It looks for messages starting with '@mention-bot'. It looks for messages starting with '@mention-bot'.
""" """
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.api_key = bot_handler.get_config_info("yoda")["api_key"] self.api_key = bot_handler.get_config_info("yoda")["api_key"]
def usage(self) -> str: def usage(self) -> str:
@ -53,7 +53,7 @@ class YodaSpeakHandler:
@mention-bot You will learn how to speak like me someday. @mention-bot You will learn how to speak like me someday.
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
self.handle_input(message, bot_handler) self.handle_input(message, bot_handler)
def send_to_yoda_api(self, sentence: str) -> str: def send_to_yoda_api(self, sentence: str) -> str:
@ -89,7 +89,7 @@ class YodaSpeakHandler:
sentence = message_content.replace(" ", "+") sentence = message_content.replace(" ", "+")
return sentence return sentence
def handle_input(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_input(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
original_content = message["content"] original_content = message["content"]
if self.is_help(original_content) or original_content == "": if self.is_help(original_content) or original_content == "":
@ -116,7 +116,7 @@ class YodaSpeakHandler:
bot_handler.send_reply(message, reply_message) bot_handler.send_reply(message, reply_message)
def send_message( def send_message(
self, bot_handler: BotHandler, message: str, stream: str, subject: str self, bot_handler: AbstractBotHandler, message: str, stream: str, subject: str
) -> None: ) -> None:
# function for sending a message # function for sending a message
bot_handler.send_message(dict(type="stream", to=stream, subject=subject, content=message)) bot_handler.send_message(dict(type="stream", to=stream, subject=subject, content=message))

View file

@ -4,7 +4,7 @@ from typing import Dict, List, Optional, Tuple, Union
import requests import requests
from requests.exceptions import ConnectionError, HTTPError from requests.exceptions import ConnectionError, HTTPError
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
commands_list = ("list", "top", "help") commands_list = ("list", "top", "help")
@ -28,7 +28,7 @@ class YoutubeHandler:
" * @mention-bot list funny dogs" " * @mention-bot list funny dogs"
) )
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("youtube") self.config_info = bot_handler.get_config_info("youtube")
# Check if API key is valid. If it is not valid, don't run the bot. # Check if API key is valid. If it is not valid, don't run the bot.
try: try:
@ -44,7 +44,7 @@ class YoutubeHandler:
except ConnectionError: except ConnectionError:
logging.warning("Bad connection") logging.warning("Bad connection")
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
if message["content"] == "" or message["content"] == "help": if message["content"] == "" or message["content"] == "help":
bot_handler.send_reply(message, self.help_content) bot_handler.send_reply(message, self.help_content)
else: else:

View file

@ -8,7 +8,7 @@ from typing import Any, Dict, Iterable, List, Sequence, Tuple
from typing_extensions import override from typing_extensions import override
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
class BadMoveError(Exception): class BadMoveError(Exception):
@ -204,13 +204,13 @@ class GameAdapter:
""" """
) )
def initialize(self, bot_handler: BotHandler) -> None: def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.bot_handler = bot_handler self.bot_handler = bot_handler
self.get_user_cache() self.get_user_cache()
self.email = self.bot_handler.email self.email = self.bot_handler.email
self.full_name = self.bot_handler.full_name self.full_name = self.bot_handler.full_name
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: AbstractBotHandler) -> None:
try: try:
self.bot_handler = bot_handler self.bot_handler = bot_handler
content = message["content"].strip() content = message["content"].strip()

View file

@ -92,7 +92,7 @@ class BotStorage(Protocol):
class CachedStorage: class CachedStorage:
def __init__(self, parent_storage: BotStorage, init_data: Dict[str, Any]) -> None: def __init__(self, parent_storage: BotStorage, init_data: Dict[str, Any]) -> None:
# CachedStorage is implemented solely for the context manager of any BotHandler. # CachedStorage is implemented solely for the context manager of any AbstractBotHandler.
# It has a parent_storage that is responsible of communicating with the database # It has a parent_storage that is responsible of communicating with the database
# 1. when certain data is not cached; # 1. when certain data is not cached;
# 2. when the data need to be flushed to the database. # 2. when the data need to be flushed to the database.
@ -176,7 +176,7 @@ def use_storage(storage: BotStorage, keys: List[str]) -> Iterator[BotStorage]:
cache.flush() cache.flush()
class BotHandler(Protocol): class AbstractBotHandler(Protocol):
user_id: int user_id: int
email: str email: str
full_name: str full_name: str
@ -378,7 +378,9 @@ class ExternalBotHandler:
sys.exit(message) sys.exit(message)
def extract_query_without_mention(message: Dict[str, Any], client: BotHandler) -> Optional[str]: def extract_query_without_mention(
message: Dict[str, Any], client: AbstractBotHandler
) -> Optional[str]:
""" """
If the bot is the first @mention in the message, then this function returns If the bot is the first @mention in the message, then this function returns
the stripped message with the bot's @mention removed. Otherwise, it returns None. the stripped message with the bot's @mention removed. Otherwise, it returns None.
@ -398,7 +400,7 @@ def extract_query_without_mention(message: Dict[str, Any], client: BotHandler) -
def is_private_message_but_not_group_pm( def is_private_message_but_not_group_pm(
message_dict: Dict[str, Any], current_user: BotHandler message_dict: Dict[str, Any], current_user: AbstractBotHandler
) -> bool: ) -> bool:
""" """
Checks whether a message dict represents a PM from another user. Checks whether a message dict represents a PM from another user.
@ -422,7 +424,7 @@ def display_config_file_errors(error_msg: str, config_file: str) -> None:
print(f"\nMore details here:\n\n{error_msg}\n") print(f"\nMore details here:\n\n{error_msg}\n")
def prepare_message_handler(bot: str, bot_handler: BotHandler, bot_lib_module: Any) -> Any: def prepare_message_handler(bot: str, bot_handler: AbstractBotHandler, bot_lib_module: Any) -> Any:
message_handler = bot_lib_module.handler_class() message_handler = bot_lib_module.handler_class()
if hasattr(message_handler, "validate_config"): if hasattr(message_handler, "validate_config"):
config_data = bot_handler.get_config_info(bot) config_data = bot_handler.get_config_info(bot)

View file

@ -5,7 +5,7 @@ from unittest.mock import ANY, MagicMock, create_autospec, patch
from zulip import Client from zulip import Client
from zulip_bots.lib import ( from zulip_bots.lib import (
BotHandler, AbstractBotHandler,
ExternalBotHandler, ExternalBotHandler,
StateHandler, StateHandler,
extract_query_without_mention, extract_query_without_mention,
@ -53,10 +53,10 @@ class FakeBotHandler:
def usage(self) -> str: def usage(self) -> str:
return """ return """
This is a fake bot handler that is used This is a fake bot handler that is used
to spec BotHandler mocks. to spec AbstractBotHandler mocks.
""" """
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
pass pass

View file

@ -10,7 +10,7 @@ from unittest import mock
import importlib_metadata as metadata import importlib_metadata as metadata
from typing_extensions import override from typing_extensions import override
from zulip_bots.lib import BotHandler from zulip_bots.lib import AbstractBotHandler
from zulip_botserver import server from zulip_botserver import server
from zulip_botserver.input_parameters import parse_args from zulip_botserver.input_parameters import parse_args
@ -19,7 +19,7 @@ from .server_test_lib import BotServerTestCase
class BotServerTests(BotServerTestCase): class BotServerTests(BotServerTestCase):
class MockMessageHandler: class MockMessageHandler:
def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, str], bot_handler: AbstractBotHandler) -> None:
assert message == {"key": "test message"} assert message == {"key": "test message"}
class MockLibModule: class MockLibModule: