Compare commits
10 commits
63dcc6a1db
...
1cbaa43db5
Author | SHA1 | Date | |
---|---|---|---|
|
1cbaa43db5 | ||
|
1d37ed2217 | ||
|
fb73220438 | ||
|
c04a17255b | ||
|
2675715ecb | ||
|
48c6e404de | ||
|
75bea9f96d | ||
|
43a4900e1f | ||
|
e9d8ef3b27 | ||
|
3bd99978ec |
51 changed files with 395 additions and 152 deletions
43
.github/pull_request_template.md
vendored
Normal file
43
.github/pull_request_template.md
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
<!-- Describe your pull request here.-->
|
||||
|
||||
Fixes: <!-- Issue link, or clear description.-->
|
||||
|
||||
<!-- If the PR changes output, always include screenshots or code blocks to demonstrate your changes. If it seems helpful, add a screen capture of the new functionality as well.
|
||||
|
||||
Tooling tips: https://zulip.readthedocs.io/en/latest/tutorials/screenshot-and-gif-software.html
|
||||
-->
|
||||
|
||||
**Screenshots and how this was tested:**
|
||||
|
||||
<details>
|
||||
<summary>Self-review checklist</summary>
|
||||
|
||||
<!-- Prior to submitting a PR, follow our step-by-step guide to review your own code:
|
||||
https://zulip.readthedocs.io/en/latest/contributing/code-reviewing.html#how-to-review-code -->
|
||||
|
||||
<!-- Once you create the PR, check off all the steps below that you have completed.
|
||||
If any of these steps are not relevant or you have not completed, leave them unchecked.-->
|
||||
|
||||
- [ ] [Self-reviewed](https://zulip.readthedocs.io/en/latest/contributing/code-reviewing.html#how-to-review-code) the changes for clarity and maintainability
|
||||
(variable names, code reuse, readability, etc.).
|
||||
|
||||
Communicate decisions, questions, and potential concerns.
|
||||
|
||||
- [ ] Explains differences from previous plans (e.g., issue description).
|
||||
- [ ] Highlights technical choices and bugs encountered.
|
||||
- [ ] Calls out remaining decisions and concerns.
|
||||
- [ ] Automated tests verify logic where appropriate.
|
||||
|
||||
Individual commits are ready for review (see [commit discipline](https://zulip.readthedocs.io/en/latest/contributing/commit-discipline.html)).
|
||||
|
||||
- [ ] Each commit is a coherent idea.
|
||||
- [ ] Commit message(s) explain reasoning and motivation for changes.
|
||||
|
||||
Completed manual review and testing of the following:
|
||||
|
||||
- [ ] Visual appearance of the changes.
|
||||
- [ ] Responsiveness and internationalization.
|
||||
- [ ] Strings and tooltips.
|
||||
- [ ] End-to-end functionality of buttons, interactions and flows.
|
||||
- [ ] Corner cases, error conditions, and easily imagined bugs.
|
||||
</details>
|
12
.mailmap
12
.mailmap
|
@ -1,16 +1,26 @@
|
|||
acrefoot <acrefoot@zulip.com> <acrefoot@humbughq.com>
|
||||
Aman Agrawal <f2016561@pilani.bits-pilani.ac.in>
|
||||
Anders Kaseorg <anders@zulip.com> <anders@zulipchat.com>
|
||||
Anders Kaseorg <anders@zulip.com> <andersk@mit.edu>
|
||||
Jessica McKellar <jesstess@zulip.com> <jesstess@humbughq.com>
|
||||
Jessica McKellar <jesstess@mit.edu> <jesstess@humbughq.com>
|
||||
Jessica McKellar <jesstess@mit.edu> <jesstess@zulip.com>
|
||||
Kevin Mehall <km@kevinmehall.net> <kevin@humbughq.com>
|
||||
Kevin Mehall <km@kevinmehall.net> <kevin@zulip.com>
|
||||
Luke Faraone <lfaraone@zulip.com> <lfaraone@humbughq.com>
|
||||
Reid Barton <rwbarton@gmail.com> <rwbarton@humbughq.com>
|
||||
Rein Zustand (rht) <rhtbot@protonmail.com>
|
||||
Rishi Gupta <rishig@zulip.com> <rishig@zulipchat.com>
|
||||
Scott Feeney <scott@oceanbase.org> <scott@humbughq.com>
|
||||
Scott Feeney <scott@oceanbase.org> <scott@zulip.com>
|
||||
Steve Howell <showell@zulip.com> <showell30@yahoo.com>
|
||||
Steve Howell <showell@zulip.com> <showell@zulipchat.com>
|
||||
Steve Howell <showell@zulip.com> <steve@zulip.com>
|
||||
Tim Abbott <tabbott@zulip.com> <tabbott@humbughq.com>
|
||||
Tim Abbott <tabbott@zulip.com> <tabbott@mit.edu>
|
||||
Tim Abbott <tabbott@zulip.com> <tabbott@zulipchat.com>
|
||||
umkay <ukhan@zulipchat.com> <umaimah.k@gmail.com>
|
||||
Waseem Daher <wdaher@zulip.com> <wdaher@dropbox.com>
|
||||
Waseem Daher <wdaher@zulip.com> <wdaher@humbughq.com>
|
||||
Zev Benjamin <zev@zulip.com> <zev@dropbox.com>
|
||||
Zev Benjamin <zev@zulip.com> <zev@humbughq.com>
|
||||
Zev Benjamin <zev@zulip.com> <zev@mit.edu>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
36
zulip/integrations/litellm/README.md
Normal file
36
zulip/integrations/litellm/README.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Summarize topic
|
||||
|
||||
Generate a short summary of the last 100 messages in the provided topic URL.
|
||||
|
||||
### API Keys
|
||||
|
||||
For testing you need access token from
|
||||
https://huggingface.co/settings/tokens (or set the correct env
|
||||
variable with the access token if using a different model)
|
||||
|
||||
In `~/.zuliprc` add a section named `litellm` and set the api key for
|
||||
the model you are trying to use. For example:
|
||||
|
||||
```
|
||||
[litellm]
|
||||
HUGGINGFACE_API_KEY=YOUR_API_KEY
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
$ pip install -r zulip/integrations/litellm/requirements.txt
|
||||
```
|
||||
|
||||
Just run `zulip/integrations/litellm/summarize-topic` to generate
|
||||
sample summary.
|
||||
|
||||
```bash
|
||||
$ zulip/integrations/litellm/summarize-topic --help
|
||||
usage: summarize-topic [-h] [--url URL] [--model MODEL]
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--url URL The URL to fetch content from
|
||||
--model MODEL The model name to use for summarization
|
||||
```
|
2
zulip/integrations/litellm/requirements.txt
Normal file
2
zulip/integrations/litellm/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
zulip
|
||||
litellm
|
130
zulip/integrations/litellm/summarize-topic
Executable file
130
zulip/integrations/litellm/summarize-topic
Executable file
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import urllib.parse
|
||||
from configparser import ConfigParser
|
||||
|
||||
from litellm import completion # type: ignore[import-not-found]
|
||||
|
||||
import zulip
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--url",
|
||||
type=str,
|
||||
help="The URL to fetch content from",
|
||||
default="https://chat.zulip.org/#narrow/stream/101-design/topic/more.20user.20indicators",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model",
|
||||
type=str,
|
||||
help="The model name to use for summarization",
|
||||
default="huggingface/meta-llama/Meta-Llama-3-8B-Instruct",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-tokens",
|
||||
type=int,
|
||||
help="The maximum tokens permitted in the response",
|
||||
default=100,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-messages",
|
||||
type=int,
|
||||
help="The maximum number of messages fetched from the server",
|
||||
default=100,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--verbose",
|
||||
type=bool,
|
||||
help="Print verbose debugging output",
|
||||
default=False,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
config_file = zulip.get_default_config_filename()
|
||||
if not config_file:
|
||||
print("Could not find the Zulip configuration file. Please read the provided README.")
|
||||
sys.exit()
|
||||
|
||||
client = zulip.Client(config_file=config_file)
|
||||
|
||||
config = ConfigParser()
|
||||
# Make config parser case sensitive otherwise API keys will be lowercased
|
||||
# which is not supported by litellm.
|
||||
# https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.optionxform
|
||||
config.optionxform = str # type: ignore[assignment, method-assign]
|
||||
|
||||
with open(config_file) as f:
|
||||
config.read_file(f, config_file)
|
||||
|
||||
# Set all the keys in `litellm` as environment variables.
|
||||
for key in config["litellm"]:
|
||||
if args.verbose:
|
||||
print("Setting key:", key)
|
||||
os.environ[key] = config["litellm"][key]
|
||||
|
||||
url = args.url
|
||||
model = args.model
|
||||
|
||||
base_url, narrow_hash = url.split("#")
|
||||
narrow_hash_terms = narrow_hash.split("/")
|
||||
channel = narrow_hash_terms[2].split("-")[1]
|
||||
topic = narrow_hash_terms[4]
|
||||
channel = urllib.parse.unquote(channel.replace(".", "%"))
|
||||
topic = urllib.parse.unquote(topic.replace(".", "%"))
|
||||
|
||||
narrow = [
|
||||
{"operator": "channel", "operand": channel},
|
||||
{"operator": "topic", "operand": topic},
|
||||
]
|
||||
|
||||
request = {
|
||||
"anchor": "newest",
|
||||
"num_before": args.max_messages,
|
||||
"num_after": 0,
|
||||
"narrow": narrow,
|
||||
# Fetch raw Markdown, not HTML
|
||||
"apply_markdown": False,
|
||||
}
|
||||
result = client.get_messages(request)
|
||||
if result["result"] == "error":
|
||||
print("Failed fetching message history", result)
|
||||
sys.exit(1)
|
||||
messages = result["messages"]
|
||||
|
||||
if len(messages) == 0:
|
||||
print("No messages in conversation to summarize")
|
||||
sys.exit(0)
|
||||
|
||||
formatted_messages = [
|
||||
{"content": f"{message['sender_full_name']}: {message['content']}", "role": "user"}
|
||||
for message in messages
|
||||
]
|
||||
|
||||
# Provide a instruction if using an `Instruct` model.
|
||||
if "Instruct" in model:
|
||||
formatted_messages.append(
|
||||
{
|
||||
"content": """
|
||||
Summarize the above content within 90 words.
|
||||
""",
|
||||
"role": "user",
|
||||
}
|
||||
)
|
||||
|
||||
# Send formatted messages to the LLM model for summarization
|
||||
response = completion(
|
||||
max_tokens=args.max_tokens,
|
||||
model=model,
|
||||
messages=formatted_messages,
|
||||
)
|
||||
|
||||
print("Summarized conversation URL:", url)
|
||||
print(
|
||||
f"Used {response['usage']['total_tokens']} tokens to summarize {len(formatted_messages)} Zulip messages."
|
||||
)
|
||||
print()
|
||||
print(response["choices"][0]["message"]["content"])
|
|
@ -177,7 +177,8 @@ def send_zulip(entry: Any, feed_name: str) -> Dict[str, Any]:
|
|||
if opts.unwrap:
|
||||
body = unwrap_text(body)
|
||||
|
||||
content = f"**[{entry.title}]({entry.link})**\n{strip_tags(body)}\n{entry.link}"
|
||||
title = f"**[{entry.title}]({entry.link})**\n" if hasattr(entry, "title") else ""
|
||||
content = f"{title}{strip_tags(body)}\n{entry.link}"
|
||||
|
||||
if opts.math:
|
||||
content = content.replace("$", "$$")
|
||||
|
|
|
@ -300,6 +300,11 @@ if "error" in res["result"]:
|
|||
logging.error(res["msg"])
|
||||
print_status_and_exit(1)
|
||||
messages = [event["message"] for event in res["events"]]
|
||||
for m in messages:
|
||||
if m.get("stream_id") is None:
|
||||
# Non-stream messages can't have a retention policy, so clean
|
||||
# them up so they don't pile up
|
||||
zulip_client.delete_message(m["id"])
|
||||
logger.info("Finished receiving Zulip messages!")
|
||||
|
||||
receive_zephyrs()
|
||||
|
|
|
@ -554,6 +554,8 @@ def send_zulip_worker(zulip_queue: "Queue[ZephyrDict]", zulip_client: zulip.Clie
|
|||
while True:
|
||||
zeph = zulip_queue.get()
|
||||
try:
|
||||
if zeph["content"] == "":
|
||||
continue
|
||||
res = send_zulip(zulip_client, zeph)
|
||||
if res.get("result") != "success":
|
||||
logger.error("Error relaying zephyr:\n%s\n%s", zeph, res)
|
||||
|
|
|
@ -548,7 +548,8 @@ class Client:
|
|||
pass
|
||||
|
||||
if vendor == "Linux":
|
||||
vendor, vendor_version, dummy = distro.linux_distribution()
|
||||
vendor = distro.name()
|
||||
vendor_version = distro.version()
|
||||
elif vendor == "Windows":
|
||||
vendor_version = platform.win32_ver()[1]
|
||||
elif vendor == "Darwin":
|
||||
|
|
|
@ -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 == []:
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"])
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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 = ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -4,10 +4,13 @@ import logging
|
|||
import re
|
||||
from typing import Any, Collection, Dict, List
|
||||
|
||||
import simple_salesforce
|
||||
# Upstream issue with simple_salesforce
|
||||
# https://github.com/simple-salesforce/simple-salesforce/issues/723
|
||||
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
|
||||
|
@ -73,9 +76,7 @@ def format_result(
|
|||
return output
|
||||
|
||||
|
||||
def query_salesforce(
|
||||
arg: str, salesforce: simple_salesforce.Salesforce, command: Dict[str, Any]
|
||||
) -> str:
|
||||
def query_salesforce(arg: str, salesforce: Salesforce, command: Dict[str, Any]) -> str:
|
||||
arg = arg.strip()
|
||||
qarg = arg.split(" -", 1)[0]
|
||||
split_args: List[str] = []
|
||||
|
@ -161,18 +162,18 @@ 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 = simple_salesforce.Salesforce(
|
||||
self.sf = Salesforce(
|
||||
username=self.config_info["username"],
|
||||
password=self.config_info["password"],
|
||||
security_token=self.config_info["security_token"],
|
||||
)
|
||||
except simple_salesforce.exceptions.SalesforceAuthenticationFailed as err:
|
||||
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)
|
||||
|
|
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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 == []:
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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() == "":
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {}"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue