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
import packaged_helloworld
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
__version__ = packaged_helloworld.__version__
@ -17,7 +17,7 @@ class HelloWorldHandler:
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"
bot_handler.send_reply(message, content)

View file

@ -4,11 +4,11 @@ from typing import Any, Dict, List
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
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.api_key = self.config_info["api_key"]
@ -38,7 +38,7 @@ class BaremetricsHandler:
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"
test_query_response = requests.get(url, headers=self.auth_header)
test_query_data = test_query_response.json()
@ -57,7 +57,7 @@ class BaremetricsHandler:
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()
if content == []:

View file

@ -4,7 +4,7 @@ from typing import Dict
import requests
from requests.exceptions import ConnectionError
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
help_message = """
You can add datapoints towards your beeminder goals \
@ -80,7 +80,7 @@ class BeeminderHandler:
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")
# Check for valid auth_token
auth_token = self.config_info["auth_token"]
@ -96,7 +96,7 @@ class BeeminderHandler:
def usage(self) -> str:
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)
bot_handler.send_reply(message, response)

View file

@ -5,7 +5,7 @@ from typing import Dict, Optional
import chess
import chess.engine
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
START_REGEX = re.compile("start with other user$")
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."
)
def initialize(self, bot_handler: BotHandler) -> None:
def initialize(self, bot_handler: AbstractBotHandler) -> None:
self.config_info = bot_handler.get_config_info("chess")
try:
@ -36,7 +36,7 @@ class ChessHandler:
# runner is testing or knows they won't be using an engine.
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"]
if content == "":
@ -76,7 +76,7 @@ class ChessHandler:
elif resign_regex_match:
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.
Replies to the bot handler.
@ -93,7 +93,7 @@ class ChessHandler:
bot_handler.storage.put("last_fen", new_board.fen())
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:
"""Starts a game with the computer. Replies to the bot handler.
@ -123,7 +123,7 @@ class ChessHandler:
)
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]:
"""Validates a board based on its FEN string. Replies to the bot
handler if there is an error with the board.
@ -147,7 +147,7 @@ class ChessHandler:
def validate_move(
self,
message: Dict[str, str],
bot_handler: BotHandler,
bot_handler: AbstractBotHandler,
last_board: chess.Board,
move_san: str,
is_computer: object,
@ -180,7 +180,7 @@ class ChessHandler:
return move
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:
"""Checks if a game is over due to
- checkmate,
@ -224,7 +224,7 @@ class ChessHandler:
return False
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:
"""Makes a move for a user in a game with another user. Replies to
the bot handler.
@ -256,7 +256,7 @@ class ChessHandler:
bot_handler.storage.put("last_fen", new_board.fen())
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:
"""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`,
@ -306,7 +306,7 @@ class ChessHandler:
bot_handler.storage.put("last_fen", new_board_after_computer_move.fen())
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:
"""Preforms a move for the computer without having the user go first in
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.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.
Parameters:

View file

@ -5,7 +5,7 @@ from math import floor, log10
from typing import Any, Dict, List
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:
@ -49,12 +49,12 @@ class ConverterHandler:
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_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"]
words = content.lower().split()

View file

@ -6,7 +6,7 @@ from typing import Dict
import html2text
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class DefineHandler:
@ -27,7 +27,7 @@ class DefineHandler:
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()
bot_response = self.get_bot_define_response(original_content)

View file

@ -5,7 +5,7 @@ from typing import Dict
import apiai
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
help_message = """DialogFlow bot
This bot will interact with dialogflow bots.
@ -47,7 +47,7 @@ class DialogFlowHandler:
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")
def usage(self) -> str:
@ -56,7 +56,7 @@ class DialogFlowHandler:
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"])
bot_handler.send_reply(message, result)

View file

@ -3,7 +3,7 @@ from typing import Any, Dict, List, Tuple
from dropbox import Dropbox
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
URL = "[{name}](https://www.dropbox.com/home{path})"
@ -14,7 +14,7 @@ class DropboxHandler:
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.ACCESS_TOKEN = self.config_info.get("access_token")
self.client = Dropbox(self.ACCESS_TOKEN)
@ -22,7 +22,7 @@ class DropboxHandler:
def usage(self) -> str:
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"]
if command == "":
command = "help"

View file

@ -1,6 +1,6 @@
from typing import Dict
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
def encrypt(text: str) -> str:
@ -34,7 +34,7 @@ class EncryptHandler:
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_handler.send_reply(message, bot_response)

View file

@ -2,7 +2,7 @@ import os
from pathlib import Path
from typing import Dict
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class FileUploaderHandler:
@ -13,7 +13,7 @@ class FileUploaderHandler:
"\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 = (
"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"

View file

@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional, Tuple
import requests
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"
SEND_MESSAGE_URL = "https://api.flock.co/v1/chat.sendMessage"
@ -97,14 +97,14 @@ class FlockHandler:
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")
def usage(self) -> str:
return """Hello from Flock Bot. You can send messages to any Flock user
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)
bot_handler.send_reply(message, response)

View file

@ -1,7 +1,7 @@
# See readme.md for instructions on running this code.
from typing import Dict
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class FollowupHandler:
@ -26,11 +26,11 @@ class FollowupHandler:
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.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"] == "":
bot_response = (
"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
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class FrontHandler:
@ -24,7 +24,7 @@ class FrontHandler:
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")
api_key = config.get("api_key")
if not api_key:
@ -32,14 +32,14 @@ class FrontHandler:
self.auth = "Bearer " + api_key
def help(self, bot_handler: BotHandler) -> str:
def help(self, bot_handler: AbstractBotHandler) -> str:
response = ""
for command, description in self.COMMANDS:
response += f"`{command}` {description}\n"
return response
def archive(self, bot_handler: BotHandler) -> str:
def archive(self, bot_handler: AbstractBotHandler) -> str:
response = requests.patch(
self.FRONT_API.format(self.conversation_id),
headers={"Authorization": self.auth},
@ -51,7 +51,7 @@ class FrontHandler:
return "Conversation was archived."
def delete(self, bot_handler: BotHandler) -> str:
def delete(self, bot_handler: AbstractBotHandler) -> str:
response = requests.patch(
self.FRONT_API.format(self.conversation_id),
headers={"Authorization": self.auth},
@ -63,7 +63,7 @@ class FrontHandler:
return "Conversation was deleted."
def spam(self, bot_handler: BotHandler) -> str:
def spam(self, bot_handler: AbstractBotHandler) -> str:
response = requests.patch(
self.FRONT_API.format(self.conversation_id),
headers={"Authorization": self.auth},
@ -75,7 +75,7 @@ class FrontHandler:
return "Conversation was marked as spam."
def restore(self, bot_handler: BotHandler) -> str:
def restore(self, bot_handler: AbstractBotHandler) -> str:
response = requests.patch(
self.FRONT_API.format(self.conversation_id),
headers={"Authorization": self.auth},
@ -87,7 +87,7 @@ class FrontHandler:
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(
self.FRONT_API.format(self.conversation_id) + "/comments",
headers={"Authorization": self.auth},
@ -99,7 +99,7 @@ class FrontHandler:
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"]
result = re.search(self.CNV_ID_REGEXP, message["subject"])

View file

@ -5,7 +5,7 @@ import requests
from requests.exceptions import ConnectionError, HTTPError
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_RANDOM_API = "http://api.giphy.com/v1/gifs/random"
@ -44,10 +44,10 @@ class GiphyHandler:
)
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")
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_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(
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:
# Each exception has a specific reply should "gif_url" return a number.
# 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
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class GithubHandler:
@ -16,7 +16,7 @@ class GithubHandler:
GITHUB_ISSUE_URL_TEMPLATE = "https://api.github.com/repos/{owner}/{repo}/issues/{id}"
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.owner = self.config_info.get("owner", False)
self.repo = self.config_info.get("repo", False)
@ -73,7 +73,7 @@ class GithubHandler:
repo = self.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
if message["content"] == "help":
bot_handler.send_reply(message, self.usage())

View file

@ -5,7 +5,7 @@ from typing import Dict, List
import requests
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]]:
@ -83,7 +83,7 @@ class GoogleSearchHandler:
@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"]
result = get_google_result(original_content)
bot_handler.send_reply(message, result)

View file

@ -2,7 +2,7 @@
from typing import Any, Dict
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class HelloWorldHandler:
@ -15,7 +15,7 @@ class HelloWorldHandler:
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"
bot_handler.send_reply(message, content)

View file

@ -1,7 +1,7 @@
# See readme.md for instructions on running this code.
from typing import Dict
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class HelpHandler:
@ -15,7 +15,7 @@ class HelpHandler:
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"
bot_handler.send_reply(message, help_content)

View file

@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
API_BASE_URL = "https://beta.idonethis.com/api/v2"
@ -147,7 +147,7 @@ More information in my help"""
class IDoneThisHandler:
def initialize(self, bot_handler: BotHandler) -> None:
def initialize(self, bot_handler: AbstractBotHandler) -> None:
global api_key, default_team # noqa: PLW0603
self.config_info = bot_handler.get_config_info("idonethis")
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
)
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))
def get_response(self, message: Dict[str, Any]) -> str:

View file

@ -2,7 +2,7 @@ import json
import re
from typing import Any, Dict, Tuple
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
QUESTION = "How should we handle this?"
@ -28,7 +28,7 @@ class IncidentHandler:
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"]
if query.startswith("new "):
start_new_incident(query, message, bot_handler)
@ -46,7 +46,9 @@ class IncidentHandler:
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
# system. We just simulate everything by having an incident id that
# we generate here.

View file

@ -2,7 +2,7 @@
from typing import Dict, Final
from zulip_bots.lib import BotHandler, use_storage
from zulip_bots.lib import AbstractBotHandler, use_storage
class IncrementorHandler:
@ -19,13 +19,13 @@ class IncrementorHandler:
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
if not storage.contains("number") or not storage.contains("message_id"):
storage.put("number", 0)
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:
num = storage.get("number")

View file

@ -4,7 +4,7 @@ from typing import Any, Dict, Optional
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
GET_REGEX = re.compile('get "(?P<issue_key>.+)"$')
CREATE_REGEX = re.compile(
@ -153,7 +153,7 @@ class JiraHandler:
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")
username = config.get("username")
@ -205,7 +205,7 @@ class JiraHandler:
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")
response = ""

View file

@ -3,7 +3,7 @@ from typing import Any, Dict
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class LinkShortenerHandler:
@ -18,11 +18,11 @@ class LinkShortenerHandler:
"`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.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")
try:
if self.is_invalid_token_error(test_request_data):
@ -38,7 +38,7 @@ class LinkShortenerHandler:
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 = (
r"("
r"(?:http|https):\/\/" # This allows for the HTTP or HTTPS

View file

@ -4,18 +4,18 @@ from typing import Any, Dict, List
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
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.access_token = self.config_info["access_token"]
self.account_id = ""
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 = {
"Authorization": "Bearer " + self.access_token,
"Accept-Version": "1.15",
@ -43,7 +43,7 @@ class MentionHandler:
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()
if message["content"].lower() == "help":

View file

@ -2,7 +2,7 @@ import logging
from typing import Dict
from zulip_bots.bots.monkeytestit.lib import parse
from zulip_bots.lib import BotHandler, NoBotConfigError
from zulip_bots.lib import AbstractBotHandler, NoBotConfigError
class MonkeyTestitBot:
@ -17,7 +17,7 @@ class MonkeyTestitBot:
"Check doc.md for more options and setup instructions."
)
def initialize(self, bot_handler: BotHandler) -> None:
def initialize(self, bot_handler: AbstractBotHandler) -> None:
try:
self.config = bot_handler.get_config_info("monkeytestit")
except NoBotConfigError:
@ -47,7 +47,7 @@ class MonkeyTestitBot:
" 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"]
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 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
This bot can do simple salesforce query requests
@ -162,7 +162,7 @@ class SalesforceHandler:
return "Usage: {} [arguments]".format(command["template"])
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")
try:
self.sf = Salesforce(
@ -173,7 +173,7 @@ class SalesforceHandler:
except SalesforceAuthenticationFailed as err:
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:
bot_response = self.get_salesforce_response(message["content"])
bot_handler.send_reply(message, bot_response)

View file

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

View file

@ -2,7 +2,7 @@ from typing import Dict
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
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"]
if msg in ("help", ""):
bot_handler.send_reply(message, self.usage())

View file

@ -2,7 +2,7 @@ from typing import Any, Dict, List
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
supported_commands = [
("help", "Get the bot usage information."),
@ -18,7 +18,7 @@ RESPONSE_ERROR_MESSAGE = "Invalid Response. Please check configuration and param
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.api_key = self.config_info["api_key"]
self.access_token = self.config_info["access_token"]
@ -28,7 +28,7 @@ class TrelloHandler:
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(
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.
"""
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()
if content == []:

View file

@ -6,7 +6,7 @@ from typing import Any, Dict, Tuple
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class NotAvailableError(Exception):
@ -23,7 +23,7 @@ class TriviaQuizHandler:
This plugin will give users a trivia question from
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"]
if query == "new":
try:
@ -59,11 +59,11 @@ class TriviaQuizHandler:
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)
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_id = generate_quiz_id(bot_handler.storage)
bot_response = format_quiz_for_markdown(quiz_id, quiz)
@ -188,7 +188,7 @@ Q: {question}
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))
@ -203,7 +203,11 @@ def build_response(is_correct: bool, num_answers: int) -> str:
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]:
answer = quiz["answers"][quiz["correct_letter"]]
is_new_answer = option not in quiz["answered_options"]

View file

@ -2,7 +2,7 @@ from typing import Dict
import tweepy
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class TwitpostBot:
@ -21,7 +21,7 @@ class TwitpostBot:
" * @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")
auth = tweepy.OAuthHandler(
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())
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"]
if content.strip() == "":

View file

@ -4,7 +4,7 @@ import os
import re
from typing import Any, Dict, Final, List, Set, Tuple, Union
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class VirtualFsHandler:
@ -16,7 +16,7 @@ class VirtualFsHandler:
def usage(self) -> str:
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"]
if command == "":
command = "help"

View file

@ -3,18 +3,18 @@ from typing import Any, Dict
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
api_url = "http://api.openweathermap.org/data/2.5/weather"
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.response_pattern = "Weather in {}, {}:\n{:.2f} F / {:.2f} C\n{}"
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)
test_response = requests.get(api_url, params=api_params)
try:
@ -29,7 +29,7 @@ class WeatherHandler:
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 = """
This bot returns weather info for specified city.
You specify city in the following format:

View file

@ -3,7 +3,7 @@ from typing import Dict, Final
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
# See readme.md for instructions on running this code.
@ -33,11 +33,13 @@ class WikipediaHandler:
should preface searches with "@mention-bot".
@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_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."""
help_text = "Please enter your search term after {}"

View file

@ -6,7 +6,7 @@ from typing import Any, Callable, Dict, Optional
import wit
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
class WitaiHandler:
@ -16,7 +16,7 @@ class WitaiHandler:
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")
token = config.get("token")
@ -37,7 +37,7 @@ class WitaiHandler:
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":
bot_handler.send_reply(message, self.help_message)
return

View file

@ -4,7 +4,7 @@ from typing import Dict, Final, Optional
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
XKCD_TEMPLATE_URL = "https://xkcd.com/%s/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`.
"""
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
xkcd_bot_response = get_xkcd_bot_response(message, quoted_name)
bot_handler.send_reply(message, xkcd_bot_response)

View file

@ -5,7 +5,7 @@ from typing import Dict
import requests
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
HELP_MESSAGE = """
This bot allows users to translate a sentence into
@ -36,7 +36,7 @@ class YodaSpeakHandler:
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"]
def usage(self) -> str:
@ -53,7 +53,7 @@ class YodaSpeakHandler:
@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)
def send_to_yoda_api(self, sentence: str) -> str:
@ -89,7 +89,7 @@ class YodaSpeakHandler:
sentence = message_content.replace(" ", "+")
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"]
if self.is_help(original_content) or original_content == "":
@ -116,7 +116,7 @@ class YodaSpeakHandler:
bot_handler.send_reply(message, reply_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:
# function for sending a 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
from requests.exceptions import ConnectionError, HTTPError
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
commands_list = ("list", "top", "help")
@ -28,7 +28,7 @@ class YoutubeHandler:
" * @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")
# Check if API key is valid. If it is not valid, don't run the bot.
try:
@ -44,7 +44,7 @@ class YoutubeHandler:
except ConnectionError:
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":
bot_handler.send_reply(message, self.help_content)
else:

View file

@ -8,7 +8,7 @@ from typing import Any, Dict, Iterable, List, Sequence, Tuple
from typing_extensions import override
from zulip_bots.lib import BotHandler
from zulip_bots.lib import AbstractBotHandler
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.get_user_cache()
self.email = self.bot_handler.email
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:
self.bot_handler = bot_handler
content = message["content"].strip()

View file

@ -92,7 +92,7 @@ class BotStorage(Protocol):
class CachedStorage:
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
# 1. when certain data is not cached;
# 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()
class BotHandler(Protocol):
class AbstractBotHandler(Protocol):
user_id: int
email: str
full_name: str
@ -378,7 +378,9 @@ class ExternalBotHandler:
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
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(
message_dict: Dict[str, Any], current_user: BotHandler
message_dict: Dict[str, Any], current_user: AbstractBotHandler
) -> bool:
"""
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")
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()
if hasattr(message_handler, "validate_config"):
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_bots.lib import (
BotHandler,
AbstractBotHandler,
ExternalBotHandler,
StateHandler,
extract_query_without_mention,
@ -53,10 +53,10 @@ class FakeBotHandler:
def usage(self) -> str:
return """
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

View file

@ -10,7 +10,7 @@ from unittest import mock
import importlib_metadata as metadata
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.input_parameters import parse_args
@ -19,7 +19,7 @@ from .server_test_lib import BotServerTestCase
class BotServerTests(BotServerTestCase):
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"}
class MockLibModule: