2023-03-10 00:29:12 +00:00
|
|
|
import asyncio
|
2023-03-05 14:07:25 +00:00
|
|
|
import os
|
2023-06-05 03:27:37 +00:00
|
|
|
from pathlib import Path
|
2023-04-13 15:22:24 +00:00
|
|
|
import re
|
|
|
|
import sys
|
2023-04-10 02:52:18 +00:00
|
|
|
import traceback
|
2023-04-20 07:39:14 +00:00
|
|
|
from typing import Union, Optional
|
2023-05-30 01:59:25 +00:00
|
|
|
import uuid
|
2023-04-13 15:22:24 +00:00
|
|
|
|
|
|
|
import aiohttp
|
2023-05-30 01:59:25 +00:00
|
|
|
from nio import (
|
|
|
|
AsyncClient,
|
|
|
|
AsyncClientConfig,
|
|
|
|
InviteMemberEvent,
|
|
|
|
JoinError,
|
|
|
|
KeyVerificationCancel,
|
|
|
|
KeyVerificationEvent,
|
|
|
|
EncryptionError,
|
|
|
|
KeyVerificationKey,
|
|
|
|
KeyVerificationMac,
|
|
|
|
KeyVerificationStart,
|
|
|
|
LocalProtocolError,
|
|
|
|
LoginResponse,
|
|
|
|
MatrixRoom,
|
|
|
|
MegolmEvent,
|
|
|
|
RoomMessageText,
|
|
|
|
ToDeviceError,
|
|
|
|
)
|
2023-03-05 14:07:25 +00:00
|
|
|
from nio.store.database import SqliteStore
|
2023-04-13 15:22:24 +00:00
|
|
|
|
2023-04-10 02:52:18 +00:00
|
|
|
from askgpt import askGPT
|
2023-06-05 03:27:37 +00:00
|
|
|
from chatgpt_bing import GPTBOT
|
2023-04-10 17:04:06 +00:00
|
|
|
from BingImageGen import ImageGenAsync
|
2023-04-13 15:22:24 +00:00
|
|
|
from log import getlogger
|
2023-03-22 14:28:22 +00:00
|
|
|
from send_image import send_room_image
|
2023-04-13 15:22:24 +00:00
|
|
|
from send_message import send_room_message
|
|
|
|
from bard import Bardbot
|
2023-05-26 12:31:52 +00:00
|
|
|
from flowise import flowise_query
|
2023-06-05 03:27:37 +00:00
|
|
|
from pandora_api import Pandora
|
2023-04-10 02:52:18 +00:00
|
|
|
|
2023-03-10 13:43:18 +00:00
|
|
|
logger = getlogger()
|
2023-06-05 03:27:37 +00:00
|
|
|
chatgpt_api_endpoint = "https://api.openai.com/v1/chat/completions"
|
|
|
|
base_path = Path(os.path.dirname(__file__)).parent
|
2023-03-10 10:53:51 +00:00
|
|
|
|
2023-03-05 14:07:25 +00:00
|
|
|
|
|
|
|
class Bot:
|
|
|
|
def __init__(
|
2023-03-10 15:45:38 +00:00
|
|
|
self,
|
|
|
|
homeserver: str,
|
|
|
|
user_id: str,
|
|
|
|
device_id: str,
|
2023-06-05 03:27:37 +00:00
|
|
|
api_endpoint: Optional[str] = None,
|
|
|
|
openai_api_key: Union[str, None] = None,
|
|
|
|
temperature: Union[float, None] = None,
|
2023-04-10 13:00:22 +00:00
|
|
|
room_id: Union[str, None] = None,
|
2023-04-10 02:52:18 +00:00
|
|
|
password: Union[str, None] = None,
|
|
|
|
access_token: Union[str, None] = None,
|
2023-04-13 15:22:24 +00:00
|
|
|
bard_token: Union[str, None] = None,
|
2023-04-11 05:41:26 +00:00
|
|
|
jailbreakEnabled: Union[bool, None] = True,
|
2023-05-30 01:59:25 +00:00
|
|
|
bing_auth_cookie: Union[str, None] = "",
|
2023-04-11 05:41:26 +00:00
|
|
|
markdown_formatted: Union[bool, None] = False,
|
2023-05-20 01:46:16 +00:00
|
|
|
output_four_images: Union[bool, None] = False,
|
2023-04-20 07:39:14 +00:00
|
|
|
import_keys_path: Optional[str] = None,
|
|
|
|
import_keys_password: Optional[str] = None,
|
2023-05-26 12:31:52 +00:00
|
|
|
flowise_api_url: Optional[str] = None,
|
2023-05-30 01:59:25 +00:00
|
|
|
flowise_api_key: Optional[str] = None,
|
|
|
|
pandora_api_endpoint: Optional[str] = None,
|
|
|
|
pandora_api_model: Optional[str] = None,
|
2023-03-05 14:07:25 +00:00
|
|
|
):
|
2023-05-30 01:59:25 +00:00
|
|
|
if homeserver is None or user_id is None or device_id is None:
|
2023-04-10 13:00:22 +00:00
|
|
|
logger.warning("homeserver && user_id && device_id is required")
|
|
|
|
sys.exit(1)
|
|
|
|
|
2023-05-30 01:59:25 +00:00
|
|
|
if password is None and access_token is None:
|
2023-04-11 05:41:26 +00:00
|
|
|
logger.warning("password or access_toekn is required")
|
2023-04-10 13:00:22 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2023-03-05 14:07:25 +00:00
|
|
|
self.homeserver = homeserver
|
|
|
|
self.user_id = user_id
|
|
|
|
self.password = password
|
2023-04-10 02:52:18 +00:00
|
|
|
self.access_token = access_token
|
2023-04-13 15:22:24 +00:00
|
|
|
self.bard_token = bard_token
|
2023-03-05 14:07:25 +00:00
|
|
|
self.device_id = device_id
|
|
|
|
self.room_id = room_id
|
2023-06-05 03:27:37 +00:00
|
|
|
self.openai_api_key = openai_api_key
|
|
|
|
self.bing_auth_cookie = bing_auth_cookie
|
|
|
|
self.api_endpoint = api_endpoint
|
2023-04-20 07:39:14 +00:00
|
|
|
self.import_keys_path = import_keys_path
|
|
|
|
self.import_keys_password = import_keys_password
|
2023-05-26 12:31:52 +00:00
|
|
|
self.flowise_api_url = flowise_api_url
|
|
|
|
self.flowise_api_key = flowise_api_key
|
2023-06-01 01:59:27 +00:00
|
|
|
self.pandora_api_endpoint = pandora_api_endpoint
|
2023-06-05 03:27:37 +00:00
|
|
|
self.temperature = temperature
|
2023-04-11 05:41:26 +00:00
|
|
|
|
2023-04-13 13:31:11 +00:00
|
|
|
self.session = aiohttp.ClientSession()
|
|
|
|
|
2023-06-05 03:27:37 +00:00
|
|
|
if openai_api_key is not None:
|
|
|
|
if not self.openai_api_key.startswith("sk-"):
|
|
|
|
logger.warning("invalid openai api key")
|
|
|
|
sys.exit(1)
|
2023-04-11 05:41:26 +00:00
|
|
|
|
|
|
|
if jailbreakEnabled is None:
|
|
|
|
self.jailbreakEnabled = True
|
|
|
|
else:
|
|
|
|
self.jailbreakEnabled = jailbreakEnabled
|
|
|
|
|
|
|
|
if markdown_formatted is None:
|
|
|
|
self.markdown_formatted = False
|
|
|
|
else:
|
|
|
|
self.markdown_formatted = markdown_formatted
|
|
|
|
|
2023-05-20 01:46:16 +00:00
|
|
|
if output_four_images is None:
|
|
|
|
self.output_four_images = False
|
|
|
|
else:
|
|
|
|
self.output_four_images = output_four_images
|
|
|
|
|
2023-03-05 14:07:25 +00:00
|
|
|
# initialize AsyncClient object
|
2023-06-05 03:27:37 +00:00
|
|
|
self.store_path = base_path
|
2023-05-30 01:59:25 +00:00
|
|
|
self.config = AsyncClientConfig(
|
|
|
|
store=SqliteStore,
|
|
|
|
store_name="db",
|
|
|
|
store_sync_tokens=True,
|
|
|
|
encryption_enabled=True,
|
|
|
|
)
|
|
|
|
self.client = AsyncClient(
|
|
|
|
homeserver=self.homeserver,
|
|
|
|
user=self.user_id,
|
|
|
|
device_id=self.device_id,
|
|
|
|
config=self.config,
|
|
|
|
store_path=self.store_path,
|
|
|
|
)
|
2023-04-10 17:04:06 +00:00
|
|
|
|
2023-04-10 02:52:18 +00:00
|
|
|
if self.access_token is not None:
|
|
|
|
self.client.access_token = self.access_token
|
|
|
|
|
2023-04-10 17:04:06 +00:00
|
|
|
# setup event callbacks
|
2023-05-30 01:59:25 +00:00
|
|
|
self.client.add_event_callback(self.message_callback, (RoomMessageText,))
|
|
|
|
self.client.add_event_callback(self.decryption_failure, (MegolmEvent,))
|
|
|
|
self.client.add_event_callback(self.invite_callback, (InviteMemberEvent,))
|
2023-04-10 17:04:06 +00:00
|
|
|
self.client.add_to_device_callback(
|
2023-05-30 01:59:25 +00:00
|
|
|
self.to_device_callback, (KeyVerificationEvent,)
|
|
|
|
)
|
2023-04-10 13:00:22 +00:00
|
|
|
|
2023-05-20 01:46:16 +00:00
|
|
|
# regular expression to match keyword commands
|
2023-03-05 14:07:25 +00:00
|
|
|
self.gpt_prog = re.compile(r"^\s*!gpt\s*(.+)$")
|
|
|
|
self.chat_prog = re.compile(r"^\s*!chat\s*(.+)$")
|
2023-03-10 15:45:38 +00:00
|
|
|
self.bing_prog = re.compile(r"^\s*!bing\s*(.+)$")
|
2023-04-13 15:22:24 +00:00
|
|
|
self.bard_prog = re.compile(r"^\s*!bard\s*(.+)$")
|
2023-03-22 14:28:22 +00:00
|
|
|
self.pic_prog = re.compile(r"^\s*!pic\s*(.+)$")
|
2023-05-26 12:31:52 +00:00
|
|
|
self.lc_prog = re.compile(r"^\s*!lc\s*(.+)$")
|
2023-03-22 14:28:22 +00:00
|
|
|
self.help_prog = re.compile(r"^\s*!help\s*.*$")
|
2023-05-30 01:59:25 +00:00
|
|
|
self.talk_prog = re.compile(r"^\s*!talk\s*(.+)$")
|
|
|
|
self.goon_prog = re.compile(r"^\s*!goon\s*.*$")
|
2023-06-05 03:27:37 +00:00
|
|
|
self.new_prog = re.compile(r"^\s*!new\s*(.+)$")
|
2023-03-10 10:53:51 +00:00
|
|
|
|
2023-04-10 02:52:18 +00:00
|
|
|
# initialize askGPT class
|
2023-04-13 13:31:11 +00:00
|
|
|
self.askgpt = askGPT(self.session)
|
2023-06-05 03:27:37 +00:00
|
|
|
# request header for !gpt command
|
|
|
|
self.gptheaders = {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
"Authorization": f"Bearer {self.openai_api_key}",
|
|
|
|
}
|
2023-04-10 02:52:18 +00:00
|
|
|
|
2023-06-05 03:27:37 +00:00
|
|
|
# initialize bing and chatgpt
|
|
|
|
if self.api_endpoint is not None:
|
|
|
|
self.gptbot = GPTBOT(self.api_endpoint, self.session)
|
|
|
|
self.chatgpt_data = {}
|
|
|
|
self.bing_data = {}
|
2023-03-10 15:45:38 +00:00
|
|
|
|
2023-04-10 17:04:06 +00:00
|
|
|
# initialize BingImageGenAsync
|
2023-05-30 01:59:25 +00:00
|
|
|
if self.bing_auth_cookie != "":
|
2023-04-10 17:04:06 +00:00
|
|
|
self.imageGen = ImageGenAsync(self.bing_auth_cookie, quiet=True)
|
|
|
|
|
2023-05-30 01:59:25 +00:00
|
|
|
# initialize pandora
|
|
|
|
if pandora_api_endpoint is not None:
|
|
|
|
self.pandora = Pandora(
|
|
|
|
api_endpoint=pandora_api_endpoint, clientSession=self.session
|
|
|
|
)
|
|
|
|
if pandora_api_model is None:
|
|
|
|
self.pandora_api_model = "text-davinci-002-render-sha-mobile"
|
|
|
|
else:
|
|
|
|
self.pandora_api_model = pandora_api_model
|
|
|
|
|
2023-06-01 01:59:27 +00:00
|
|
|
self.pandora_data = {}
|
2023-05-30 01:59:25 +00:00
|
|
|
|
2023-06-05 03:27:37 +00:00
|
|
|
# initialize bard
|
|
|
|
self.bard_data = {}
|
|
|
|
|
2023-04-13 13:31:11 +00:00
|
|
|
def __del__(self):
|
|
|
|
try:
|
2023-04-16 02:41:07 +00:00
|
|
|
loop = asyncio.get_running_loop()
|
2023-05-20 01:46:16 +00:00
|
|
|
except RuntimeError:
|
2023-04-13 13:31:11 +00:00
|
|
|
loop = asyncio.new_event_loop()
|
|
|
|
asyncio.set_event_loop(loop)
|
|
|
|
loop.run_until_complete(self._close())
|
|
|
|
|
|
|
|
async def _close(self):
|
|
|
|
await self.session.close()
|
2023-03-22 14:28:22 +00:00
|
|
|
|
2023-06-05 03:27:37 +00:00
|
|
|
def chatgpt_session_init(self, sender_id: str) -> None:
|
|
|
|
self.chatgpt_data[sender_id] = {
|
|
|
|
"first_time": True,
|
|
|
|
}
|
|
|
|
|
|
|
|
def bing_session_init(self, sender_id: str) -> None:
|
|
|
|
self.bing_data[sender_id] = {
|
|
|
|
"first_time": True,
|
|
|
|
}
|
|
|
|
|
|
|
|
def pandora_session_init(self, sender_id: str) -> None:
|
2023-05-30 01:59:25 +00:00
|
|
|
self.pandora_data[sender_id] = {
|
|
|
|
"conversation_id": None,
|
|
|
|
"parent_message_id": str(uuid.uuid4()),
|
|
|
|
"first_time": True,
|
|
|
|
}
|
|
|
|
|
2023-06-05 03:27:37 +00:00
|
|
|
async def bard_session_init(self, sender_id: str) -> None:
|
|
|
|
self.bard_data[sender_id] = {
|
|
|
|
"instance": await Bardbot.create(self.bard_token, 60),
|
|
|
|
}
|
|
|
|
|
2023-04-10 13:00:22 +00:00
|
|
|
# message_callback RoomMessageText event
|
2023-03-05 14:07:25 +00:00
|
|
|
async def message_callback(self, room: MatrixRoom, event: RoomMessageText) -> None:
|
2023-04-10 13:00:22 +00:00
|
|
|
if self.room_id is None:
|
2023-03-14 14:37:30 +00:00
|
|
|
room_id = room.room_id
|
|
|
|
else:
|
|
|
|
# if event room id does not match the room id in config, return
|
|
|
|
if room.room_id != self.room_id:
|
|
|
|
return
|
|
|
|
room_id = self.room_id
|
|
|
|
|
|
|
|
# reply event_id
|
|
|
|
reply_to_event_id = event.event_id
|
|
|
|
|
2023-04-10 02:52:18 +00:00
|
|
|
# sender_id
|
|
|
|
sender_id = event.sender
|
|
|
|
|
|
|
|
# user_message
|
|
|
|
raw_user_message = event.body
|
|
|
|
|
2023-03-10 13:43:18 +00:00
|
|
|
# print info to console
|
2023-04-20 07:39:14 +00:00
|
|
|
logger.info(
|
2023-03-10 13:43:18 +00:00
|
|
|
f"Message received in room {room.display_name}\n"
|
2023-04-10 02:52:18 +00:00
|
|
|
f"{room.user_name(event.sender)} | {raw_user_message}"
|
2023-03-10 13:43:18 +00:00
|
|
|
)
|
|
|
|
|
2023-04-10 02:52:18 +00:00
|
|
|
# prevent command trigger loop
|
2023-03-22 14:28:22 +00:00
|
|
|
if self.user_id != event.sender:
|
|
|
|
# remove newline character from event.body
|
2023-04-10 02:52:18 +00:00
|
|
|
content_body = re.sub("\r\n|\r|\n", " ", raw_user_message)
|
2023-03-10 15:45:38 +00:00
|
|
|
|
2023-06-05 03:27:37 +00:00
|
|
|
# !gpt command
|
|
|
|
if self.openai_api_key is not None:
|
|
|
|
m = self.gpt_prog.match(content_body)
|
|
|
|
if m:
|
|
|
|
prompt = m.group(1)
|
2023-04-10 13:00:22 +00:00
|
|
|
try:
|
2023-05-30 01:59:25 +00:00
|
|
|
asyncio.create_task(
|
2023-06-05 03:27:37 +00:00
|
|
|
self.gpt(
|
2023-05-30 01:59:25 +00:00
|
|
|
room_id,
|
|
|
|
reply_to_event_id,
|
|
|
|
prompt,
|
|
|
|
sender_id,
|
|
|
|
raw_user_message,
|
|
|
|
)
|
|
|
|
)
|
2023-04-10 13:00:22 +00:00
|
|
|
except Exception as e:
|
2023-04-13 13:31:11 +00:00
|
|
|
logger.error(e, exc_info=True)
|
2023-06-05 03:27:37 +00:00
|
|
|
|
|
|
|
if self.api_endpoint is not None:
|
|
|
|
# chatgpt
|
|
|
|
n = self.chat_prog.match(content_body)
|
|
|
|
if n:
|
|
|
|
if sender_id not in self.chatgpt_data:
|
|
|
|
self.chatgpt_session_init(sender_id)
|
|
|
|
prompt = n.group(1)
|
|
|
|
if self.openai_api_key is not None:
|
|
|
|
try:
|
|
|
|
asyncio.create_task(
|
|
|
|
self.chat(
|
|
|
|
room_id,
|
|
|
|
reply_to_event_id,
|
|
|
|
prompt,
|
|
|
|
sender_id,
|
|
|
|
raw_user_message,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e, exc_info=True)
|
|
|
|
else:
|
|
|
|
logger.warning("No API_KEY provided")
|
2023-05-30 01:59:25 +00:00
|
|
|
await send_room_message(
|
2023-06-05 03:27:37 +00:00
|
|
|
self.client, room_id, reply_message="API_KEY not provided"
|
2023-05-30 01:59:25 +00:00
|
|
|
)
|
2023-03-22 14:28:22 +00:00
|
|
|
|
2023-06-05 03:27:37 +00:00
|
|
|
# bing ai
|
|
|
|
# if self.bing_api_endpoint != "":
|
|
|
|
# bing ai can be used without cookie
|
2023-04-10 02:52:18 +00:00
|
|
|
b = self.bing_prog.match(content_body)
|
2023-03-22 14:28:22 +00:00
|
|
|
if b:
|
2023-06-05 03:27:37 +00:00
|
|
|
if sender_id not in self.bing_data:
|
|
|
|
self.bing_session_init(sender_id)
|
2023-03-22 14:28:22 +00:00
|
|
|
prompt = b.group(1)
|
2023-04-10 02:52:18 +00:00
|
|
|
# raw_content_body used for construct formatted_body
|
2023-04-10 13:00:22 +00:00
|
|
|
try:
|
2023-05-30 01:59:25 +00:00
|
|
|
asyncio.create_task(
|
|
|
|
self.bing(
|
|
|
|
room_id,
|
|
|
|
reply_to_event_id,
|
|
|
|
prompt,
|
|
|
|
sender_id,
|
|
|
|
raw_user_message,
|
|
|
|
)
|
2023-04-14 11:03:34 +00:00
|
|
|
)
|
2023-04-10 13:00:22 +00:00
|
|
|
except Exception as e:
|
2023-04-13 13:31:11 +00:00
|
|
|
logger.error(e, exc_info=True)
|
2023-03-22 14:28:22 +00:00
|
|
|
|
|
|
|
# Image Generation by Microsoft Bing
|
2023-05-30 01:59:25 +00:00
|
|
|
if self.bing_auth_cookie != "":
|
2023-04-10 02:52:18 +00:00
|
|
|
i = self.pic_prog.match(content_body)
|
2023-03-22 14:28:22 +00:00
|
|
|
if i:
|
|
|
|
prompt = i.group(1)
|
2023-04-10 13:00:22 +00:00
|
|
|
try:
|
2023-04-14 11:03:34 +00:00
|
|
|
asyncio.create_task(self.pic(room_id, prompt))
|
2023-04-10 13:00:22 +00:00
|
|
|
except Exception as e:
|
2023-04-13 13:31:11 +00:00
|
|
|
logger.error(e, exc_info=True)
|
2023-03-22 14:28:22 +00:00
|
|
|
|
2023-04-13 15:22:24 +00:00
|
|
|
# Google's Bard
|
|
|
|
if self.bard_token is not None:
|
2023-06-05 03:27:37 +00:00
|
|
|
if sender_id not in self.bard_data:
|
|
|
|
await self.bard_session_init(sender_id)
|
2023-04-13 15:22:24 +00:00
|
|
|
b = self.bard_prog.match(content_body)
|
|
|
|
if b:
|
|
|
|
prompt = b.group(1)
|
|
|
|
try:
|
2023-05-30 01:59:25 +00:00
|
|
|
asyncio.create_task(
|
|
|
|
self.bard(
|
|
|
|
room_id,
|
|
|
|
reply_to_event_id,
|
|
|
|
prompt,
|
|
|
|
sender_id,
|
|
|
|
raw_user_message,
|
|
|
|
)
|
2023-04-14 11:03:34 +00:00
|
|
|
)
|
2023-04-13 15:22:24 +00:00
|
|
|
except Exception as e:
|
|
|
|
logger.error(e, exc_info=True)
|
|
|
|
|
2023-05-26 12:31:52 +00:00
|
|
|
# lc command
|
|
|
|
if self.flowise_api_url is not None:
|
|
|
|
m = self.lc_prog.match(content_body)
|
|
|
|
if m:
|
|
|
|
prompt = m.group(1)
|
|
|
|
try:
|
2023-05-30 01:59:25 +00:00
|
|
|
asyncio.create_task(
|
|
|
|
self.lc(
|
|
|
|
room_id,
|
|
|
|
reply_to_event_id,
|
|
|
|
prompt,
|
|
|
|
sender_id,
|
|
|
|
raw_user_message,
|
|
|
|
)
|
2023-05-26 12:31:52 +00:00
|
|
|
)
|
2023-05-30 01:59:25 +00:00
|
|
|
except Exception as e:
|
|
|
|
await send_room_message(self.client, room_id, reply_message={e})
|
2023-06-05 03:27:37 +00:00
|
|
|
logger.error(e, exc_info=True)
|
2023-05-30 01:59:25 +00:00
|
|
|
|
|
|
|
# pandora
|
|
|
|
if self.pandora_api_endpoint is not None:
|
|
|
|
t = self.talk_prog.match(content_body)
|
|
|
|
if t:
|
2023-06-05 03:27:37 +00:00
|
|
|
if sender_id not in self.pandora_data:
|
|
|
|
self.pandora_session_init(sender_id)
|
2023-05-30 01:59:25 +00:00
|
|
|
prompt = t.group(1)
|
|
|
|
try:
|
|
|
|
asyncio.create_task(
|
|
|
|
self.talk(
|
|
|
|
room_id,
|
|
|
|
reply_to_event_id,
|
|
|
|
prompt,
|
|
|
|
sender_id,
|
|
|
|
raw_user_message,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e, exc_info=True)
|
|
|
|
|
|
|
|
g = self.goon_prog.match(content_body)
|
|
|
|
if g:
|
2023-06-05 03:27:37 +00:00
|
|
|
if sender_id not in self.pandora_data:
|
|
|
|
self.pandora_session_init(sender_id)
|
2023-05-30 01:59:25 +00:00
|
|
|
try:
|
|
|
|
asyncio.create_task(
|
|
|
|
self.goon(
|
|
|
|
room_id,
|
|
|
|
reply_to_event_id,
|
|
|
|
sender_id,
|
|
|
|
raw_user_message,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e, exc_info=True)
|
|
|
|
|
2023-06-05 03:27:37 +00:00
|
|
|
# !new command
|
|
|
|
n = self.new_prog.match(content_body)
|
|
|
|
if n:
|
|
|
|
new_command_kind = n.group(1)
|
|
|
|
try:
|
|
|
|
asyncio.create_task(
|
|
|
|
self.new(
|
|
|
|
room_id,
|
|
|
|
reply_to_event_id,
|
|
|
|
sender_id,
|
|
|
|
raw_user_message,
|
|
|
|
new_command_kind,
|
2023-05-26 12:31:52 +00:00
|
|
|
)
|
2023-06-05 03:27:37 +00:00
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e, exc_info=True)
|
2023-05-26 12:31:52 +00:00
|
|
|
|
2023-03-22 14:28:22 +00:00
|
|
|
# help command
|
2023-04-10 02:52:18 +00:00
|
|
|
h = self.help_prog.match(content_body)
|
2023-03-22 14:28:22 +00:00
|
|
|
if h:
|
2023-06-05 03:27:37 +00:00
|
|
|
try:
|
|
|
|
asyncio.create_task(self.help(room_id))
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e, exc_info=True)
|
2023-03-22 14:28:22 +00:00
|
|
|
|
2023-04-10 13:00:22 +00:00
|
|
|
# message_callback decryption_failure event
|
|
|
|
async def decryption_failure(self, room: MatrixRoom, event: MegolmEvent) -> None:
|
|
|
|
if not isinstance(event, MegolmEvent):
|
|
|
|
return
|
|
|
|
|
|
|
|
logger.error(
|
2023-05-20 01:46:16 +00:00
|
|
|
f"Failed to decrypt message: {event.event_id} \
|
2023-05-30 01:59:25 +00:00
|
|
|
from {event.sender} in {room.room_id}\n"
|
|
|
|
+ "Please make sure the bot current session is verified"
|
2023-04-10 17:04:06 +00:00
|
|
|
)
|
2023-04-10 13:00:22 +00:00
|
|
|
|
2023-04-10 02:52:18 +00:00
|
|
|
# invite_callback event
|
|
|
|
async def invite_callback(self, room: MatrixRoom, event: InviteMemberEvent) -> None:
|
|
|
|
"""Handle an incoming invite event.
|
|
|
|
If an invite is received, then join the room specified in the invite.
|
2023-04-16 02:41:07 +00:00
|
|
|
code copied from: https://github.com/8go/matrix-eno-bot/blob/ad037e02bd2960941109e9526c1033dd157bb212/callbacks.py#L104
|
2023-04-10 02:52:18 +00:00
|
|
|
"""
|
|
|
|
logger.debug(f"Got invite to {room.room_id} from {event.sender}.")
|
|
|
|
# Attempt to join 3 times before giving up
|
|
|
|
for attempt in range(3):
|
|
|
|
result = await self.client.join(room.room_id)
|
|
|
|
if type(result) == JoinError:
|
|
|
|
logger.error(
|
|
|
|
f"Error joining room {room.room_id} (attempt %d): %s",
|
2023-05-30 01:59:25 +00:00
|
|
|
attempt,
|
|
|
|
result.message,
|
2023-04-10 02:52:18 +00:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
logger.error("Unable to join room: %s", room.room_id)
|
|
|
|
|
|
|
|
# Successfully joined room
|
|
|
|
logger.info(f"Joined {room.room_id}")
|
2023-04-10 17:04:06 +00:00
|
|
|
|
2023-04-10 02:52:18 +00:00
|
|
|
# to_device_callback event
|
|
|
|
async def to_device_callback(self, event: KeyVerificationEvent) -> None:
|
|
|
|
"""Handle events sent to device.
|
|
|
|
|
|
|
|
Specifically this will perform Emoji verification.
|
|
|
|
It will accept an incoming Emoji verification requests
|
|
|
|
and follow the verification protocol.
|
|
|
|
code copied from: https://github.com/8go/matrix-eno-bot/blob/ad037e02bd2960941109e9526c1033dd157bb212/callbacks.py#L127
|
|
|
|
"""
|
2023-03-22 14:28:22 +00:00
|
|
|
try:
|
2023-04-10 02:52:18 +00:00
|
|
|
client = self.client
|
|
|
|
logger.debug(
|
2023-05-30 01:59:25 +00:00
|
|
|
f"Device Event of type {type(event)} received in " "to_device_cb()."
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
|
|
|
|
if isinstance(event, KeyVerificationStart): # first step
|
2023-05-30 01:59:25 +00:00
|
|
|
"""first step: receive KeyVerificationStart
|
2023-04-10 02:52:18 +00:00
|
|
|
KeyVerificationStart(
|
|
|
|
source={'content':
|
|
|
|
{'method': 'm.sas.v1',
|
|
|
|
'from_device': 'DEVICEIDXY',
|
|
|
|
'key_agreement_protocols':
|
|
|
|
['curve25519-hkdf-sha256', 'curve25519'],
|
|
|
|
'hashes': ['sha256'],
|
|
|
|
'message_authentication_codes':
|
|
|
|
['hkdf-hmac-sha256', 'hmac-sha256'],
|
|
|
|
'short_authentication_string':
|
|
|
|
['decimal', 'emoji'],
|
|
|
|
'transaction_id': 'SomeTxId'
|
|
|
|
},
|
|
|
|
'type': 'm.key.verification.start',
|
|
|
|
'sender': '@user2:example.org'
|
|
|
|
},
|
|
|
|
sender='@user2:example.org',
|
|
|
|
transaction_id='SomeTxId',
|
|
|
|
from_device='DEVICEIDXY',
|
|
|
|
method='m.sas.v1',
|
|
|
|
key_agreement_protocols=[
|
|
|
|
'curve25519-hkdf-sha256', 'curve25519'],
|
|
|
|
hashes=['sha256'],
|
|
|
|
message_authentication_codes=[
|
|
|
|
'hkdf-hmac-sha256', 'hmac-sha256'],
|
|
|
|
short_authentication_string=['decimal', 'emoji'])
|
|
|
|
"""
|
|
|
|
|
|
|
|
if "emoji" not in event.short_authentication_string:
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = (
|
|
|
|
"Other device does not support emoji verification "
|
|
|
|
f"{event.short_authentication_string}. Aborting."
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
return
|
2023-05-30 01:59:25 +00:00
|
|
|
resp = await client.accept_key_verification(event.transaction_id)
|
2023-04-10 02:52:18 +00:00
|
|
|
if isinstance(resp, ToDeviceError):
|
|
|
|
estr = f"accept_key_verification() failed with {resp}"
|
|
|
|
logger.info(estr)
|
|
|
|
|
|
|
|
sas = client.key_verifications[event.transaction_id]
|
|
|
|
|
|
|
|
todevice_msg = sas.share_key()
|
|
|
|
resp = await client.to_device(todevice_msg)
|
|
|
|
if isinstance(resp, ToDeviceError):
|
|
|
|
estr = f"to_device() failed with {resp}"
|
|
|
|
logger.info(estr)
|
|
|
|
|
|
|
|
elif isinstance(event, KeyVerificationCancel): # anytime
|
2023-05-30 01:59:25 +00:00
|
|
|
"""at any time: receive KeyVerificationCancel
|
2023-04-10 02:52:18 +00:00
|
|
|
KeyVerificationCancel(source={
|
|
|
|
'content': {'code': 'm.mismatched_sas',
|
|
|
|
'reason': 'Mismatched authentication string',
|
|
|
|
'transaction_id': 'SomeTxId'},
|
|
|
|
'type': 'm.key.verification.cancel',
|
|
|
|
'sender': '@user2:example.org'},
|
|
|
|
sender='@user2:example.org',
|
|
|
|
transaction_id='SomeTxId',
|
|
|
|
code='m.mismatched_sas',
|
|
|
|
reason='Mismatched short authentication string')
|
|
|
|
"""
|
|
|
|
|
|
|
|
# There is no need to issue a
|
|
|
|
# client.cancel_key_verification(tx_id, reject=False)
|
|
|
|
# here. The SAS flow is already cancelled.
|
|
|
|
# We only need to inform the user.
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = (
|
|
|
|
f"Verification has been cancelled by {event.sender} "
|
|
|
|
f'for reason "{event.reason}".'
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
|
|
|
|
elif isinstance(event, KeyVerificationKey): # second step
|
2023-05-30 01:59:25 +00:00
|
|
|
"""Second step is to receive KeyVerificationKey
|
2023-04-10 02:52:18 +00:00
|
|
|
KeyVerificationKey(
|
|
|
|
source={'content': {
|
|
|
|
'key': 'SomeCryptoKey',
|
|
|
|
'transaction_id': 'SomeTxId'},
|
|
|
|
'type': 'm.key.verification.key',
|
|
|
|
'sender': '@user2:example.org'
|
|
|
|
},
|
|
|
|
sender='@user2:example.org',
|
|
|
|
transaction_id='SomeTxId',
|
|
|
|
key='SomeCryptoKey')
|
|
|
|
"""
|
|
|
|
sas = client.key_verifications[event.transaction_id]
|
|
|
|
|
2023-04-20 07:39:14 +00:00
|
|
|
logger.info(f"{sas.get_emoji()}")
|
2023-04-10 02:52:18 +00:00
|
|
|
# don't log the emojis
|
|
|
|
|
|
|
|
# The bot process must run in forground with a screen and
|
|
|
|
# keyboard so that user can accept/reject via keyboard.
|
|
|
|
# For emoji verification bot must not run as service or
|
|
|
|
# in background.
|
2023-04-10 17:04:06 +00:00
|
|
|
# yn = input("Do the emojis match? (Y/N) (C for Cancel) ")
|
|
|
|
# automatic match, so we use y
|
|
|
|
yn = "y"
|
2023-04-10 02:52:18 +00:00
|
|
|
if yn.lower() == "y":
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = (
|
|
|
|
"Match! The verification for this " "device will be accepted."
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
2023-05-30 01:59:25 +00:00
|
|
|
resp = await client.confirm_short_auth_string(event.transaction_id)
|
2023-04-10 02:52:18 +00:00
|
|
|
if isinstance(resp, ToDeviceError):
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = "confirm_short_auth_string() " f"failed with {resp}"
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
elif yn.lower() == "n": # no, don't match, reject
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = (
|
|
|
|
"No match! Device will NOT be verified "
|
|
|
|
"by rejecting verification."
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
resp = await client.cancel_key_verification(
|
2023-05-30 01:59:25 +00:00
|
|
|
event.transaction_id, reject=True
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
if isinstance(resp, ToDeviceError):
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = f"cancel_key_verification failed with {resp}"
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
else: # C or anything for cancel
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = "Cancelled by user! Verification will be " "cancelled."
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
resp = await client.cancel_key_verification(
|
2023-05-30 01:59:25 +00:00
|
|
|
event.transaction_id, reject=False
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
if isinstance(resp, ToDeviceError):
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = f"cancel_key_verification failed with {resp}"
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
|
|
|
|
elif isinstance(event, KeyVerificationMac): # third step
|
2023-05-30 01:59:25 +00:00
|
|
|
"""Third step is to receive KeyVerificationMac
|
2023-04-10 02:52:18 +00:00
|
|
|
KeyVerificationMac(
|
|
|
|
source={'content': {
|
|
|
|
'mac': {'ed25519:DEVICEIDXY': 'SomeKey1',
|
|
|
|
'ed25519:SomeKey2': 'SomeKey3'},
|
|
|
|
'keys': 'SomeCryptoKey4',
|
|
|
|
'transaction_id': 'SomeTxId'},
|
|
|
|
'type': 'm.key.verification.mac',
|
|
|
|
'sender': '@user2:example.org'},
|
|
|
|
sender='@user2:example.org',
|
|
|
|
transaction_id='SomeTxId',
|
|
|
|
mac={'ed25519:DEVICEIDXY': 'SomeKey1',
|
|
|
|
'ed25519:SomeKey2': 'SomeKey3'},
|
|
|
|
keys='SomeCryptoKey4')
|
|
|
|
"""
|
|
|
|
sas = client.key_verifications[event.transaction_id]
|
|
|
|
try:
|
|
|
|
todevice_msg = sas.get_mac()
|
|
|
|
except LocalProtocolError as e:
|
|
|
|
# e.g. it might have been cancelled by ourselves
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = (
|
|
|
|
f"Cancelled or protocol error: Reason: {e}.\n"
|
|
|
|
f"Verification with {event.sender} not concluded. "
|
|
|
|
"Try again?"
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
else:
|
|
|
|
resp = await client.to_device(todevice_msg)
|
|
|
|
if isinstance(resp, ToDeviceError):
|
|
|
|
estr = f"to_device failed with {resp}"
|
|
|
|
logger.info(estr)
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = (
|
|
|
|
f"sas.we_started_it = {sas.we_started_it}\n"
|
|
|
|
f"sas.sas_accepted = {sas.sas_accepted}\n"
|
|
|
|
f"sas.canceled = {sas.canceled}\n"
|
|
|
|
f"sas.timed_out = {sas.timed_out}\n"
|
|
|
|
f"sas.verified = {sas.verified}\n"
|
|
|
|
f"sas.verified_devices = {sas.verified_devices}\n"
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = (
|
|
|
|
"Emoji verification was successful!\n"
|
|
|
|
"Initiate another Emoji verification from "
|
|
|
|
"another device or room if desired. "
|
|
|
|
"Or if done verifying, hit Control-C to stop the "
|
|
|
|
"bot in order to restart it as a service or to "
|
|
|
|
"run it in the background."
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
else:
|
2023-05-30 01:59:25 +00:00
|
|
|
estr = (
|
|
|
|
f"Received unexpected event type {type(event)}. "
|
|
|
|
f"Event is {event}. Event will be ignored."
|
|
|
|
)
|
2023-04-10 02:52:18 +00:00
|
|
|
logger.info(estr)
|
|
|
|
except BaseException:
|
|
|
|
estr = traceback.format_exc()
|
|
|
|
logger.info(estr)
|
|
|
|
|
|
|
|
# !chat command
|
2023-05-30 01:59:25 +00:00
|
|
|
async def chat(
|
|
|
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
|
|
|
):
|
2023-06-05 03:27:37 +00:00
|
|
|
try:
|
|
|
|
await self.client.room_typing(room_id, timeout=300000)
|
|
|
|
if (
|
|
|
|
self.chatgpt_data[sender_id]["first_time"]
|
|
|
|
or "conversationId" not in self.chatgpt_data[sender_id]
|
|
|
|
):
|
|
|
|
self.chatgpt_data[sender_id]["first_time"] = False
|
|
|
|
payload = {
|
|
|
|
"message": prompt,
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
payload = {
|
|
|
|
"message": prompt,
|
|
|
|
"conversationId": self.chatgpt_data[sender_id]["conversationId"],
|
|
|
|
"parentMessageId": self.chatgpt_data[sender_id]["parentMessageId"],
|
|
|
|
}
|
|
|
|
payload.update(
|
|
|
|
{
|
|
|
|
"clientOptions": {
|
|
|
|
"clientToUse": "chatgpt",
|
|
|
|
"openaiApiKey": self.openai_api_key,
|
|
|
|
"modelOptions": {
|
|
|
|
"temperature": self.temperature,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
resp = await self.gptbot.queryChatGPT(payload)
|
|
|
|
content = resp["response"]
|
|
|
|
self.chatgpt_data[sender_id]["conversationId"] = resp["conversationId"]
|
|
|
|
self.chatgpt_data[sender_id]["parentMessageId"] = resp["messageId"]
|
|
|
|
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message=content,
|
|
|
|
reply_to_event_id="",
|
|
|
|
sender_id=sender_id,
|
|
|
|
user_message=raw_user_message,
|
|
|
|
markdown_formatted=self.markdown_formatted,
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
await send_room_message(self.client, room_id, reply_message=str(e))
|
2023-04-10 17:04:06 +00:00
|
|
|
|
2023-04-10 02:52:18 +00:00
|
|
|
# !gpt command
|
2023-05-30 01:59:25 +00:00
|
|
|
async def gpt(
|
|
|
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
|
|
|
) -> None:
|
2023-06-05 03:27:37 +00:00
|
|
|
try:
|
|
|
|
# sending typing state
|
|
|
|
await self.client.room_typing(room_id, timeout=30000)
|
|
|
|
# timeout 300s
|
|
|
|
text = await asyncio.wait_for(
|
|
|
|
self.askgpt.oneTimeAsk(
|
|
|
|
prompt, chatgpt_api_endpoint, self.gptheaders, self.temperature
|
|
|
|
),
|
|
|
|
timeout=300,
|
|
|
|
)
|
2023-03-10 11:19:49 +00:00
|
|
|
|
2023-06-05 03:27:37 +00:00
|
|
|
text = text.strip()
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message=text,
|
|
|
|
reply_to_event_id="",
|
|
|
|
sender_id=sender_id,
|
|
|
|
user_message=raw_user_message,
|
|
|
|
markdown_formatted=self.markdown_formatted,
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message="Error encountered, please try again or contact admin.",
|
|
|
|
)
|
2023-03-22 14:28:22 +00:00
|
|
|
|
|
|
|
# !bing command
|
2023-05-30 01:59:25 +00:00
|
|
|
async def bing(
|
|
|
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
|
|
|
) -> None:
|
2023-06-05 03:27:37 +00:00
|
|
|
try:
|
|
|
|
# sending typing state
|
|
|
|
await self.client.room_typing(room_id, timeout=300000)
|
|
|
|
|
|
|
|
if (
|
|
|
|
self.bing_data[sender_id]["first_time"]
|
|
|
|
or "conversationId" not in self.bing_data[sender_id]
|
|
|
|
):
|
|
|
|
self.bing_data[sender_id]["first_time"] = False
|
|
|
|
payload = {
|
|
|
|
"message": prompt,
|
|
|
|
"clientOptions": {
|
|
|
|
"clientToUse": "bing",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
payload = {
|
|
|
|
"message": prompt,
|
|
|
|
"clientOptions": {
|
|
|
|
"clientToUse": "bing",
|
|
|
|
},
|
|
|
|
"conversationSignature": self.bing_data[sender_id][
|
|
|
|
"conversationSignature"
|
|
|
|
],
|
|
|
|
"conversationId": self.bing_data[sender_id]["conversationId"],
|
|
|
|
"clientId": self.bing_data[sender_id]["clientId"],
|
|
|
|
"invocationId": self.bing_data[sender_id]["invocationId"],
|
|
|
|
}
|
|
|
|
resp = await self.gptbot.queryBing(payload)
|
|
|
|
content = "".join(
|
|
|
|
[body["text"] for body in resp["details"]["adaptiveCards"][0]["body"]]
|
|
|
|
)
|
|
|
|
self.bing_data[sender_id]["conversationSignature"] = resp[
|
|
|
|
"conversationSignature"
|
|
|
|
]
|
|
|
|
self.bing_data[sender_id]["conversationId"] = resp["conversationId"]
|
|
|
|
self.bing_data[sender_id]["clientId"] = resp["clientId"]
|
|
|
|
self.bing_data[sender_id]["invocationId"] = resp["invocationId"]
|
|
|
|
|
|
|
|
text = content.strip()
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message=text,
|
|
|
|
reply_to_event_id="",
|
|
|
|
sender_id=sender_id,
|
|
|
|
user_message=raw_user_message,
|
|
|
|
markdown_formatted=self.markdown_formatted,
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
await send_room_message(self.client, room_id, reply_message=str(e))
|
2023-04-13 15:22:24 +00:00
|
|
|
|
|
|
|
# !bard command
|
2023-05-30 01:59:25 +00:00
|
|
|
async def bard(
|
|
|
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
|
|
|
) -> None:
|
2023-06-05 03:27:37 +00:00
|
|
|
try:
|
|
|
|
# sending typing state
|
|
|
|
await self.client.room_typing(room_id)
|
|
|
|
response = await self.bard_data[sender_id]["instance"].ask(prompt)
|
|
|
|
|
|
|
|
content = str(response["content"]).strip()
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message=content,
|
|
|
|
reply_to_event_id="",
|
|
|
|
sender_id=sender_id,
|
|
|
|
user_message=raw_user_message,
|
|
|
|
markdown_formatted=self.markdown_formatted,
|
|
|
|
)
|
|
|
|
except TimeoutError:
|
|
|
|
await send_room_message(self.client, room_id, reply_message="TimeoutError")
|
|
|
|
except Exception as e:
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message="Error calling Bard API, please contact admin.",
|
|
|
|
)
|
2023-03-10 11:19:49 +00:00
|
|
|
|
2023-05-26 12:31:52 +00:00
|
|
|
# !lc command
|
2023-05-30 01:59:25 +00:00
|
|
|
async def lc(
|
|
|
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
|
|
|
) -> None:
|
2023-06-05 03:27:37 +00:00
|
|
|
try:
|
|
|
|
# sending typing state
|
|
|
|
await self.client.room_typing(room_id)
|
|
|
|
if self.flowise_api_key is not None:
|
|
|
|
headers = {"Authorization": f"Bearer {self.flowise_api_key}"}
|
|
|
|
response = await flowise_query(self.flowise_api_url, prompt, self.session, headers)
|
|
|
|
else:
|
|
|
|
response = await flowise_query(self.flowise_api_url, prompt, self.session)
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message=response,
|
|
|
|
reply_to_event_id="",
|
|
|
|
sender_id=sender_id,
|
|
|
|
user_message=raw_user_message,
|
|
|
|
markdown_formatted=self.markdown_formatted,
|
2023-05-30 01:59:25 +00:00
|
|
|
)
|
2023-06-05 03:27:37 +00:00
|
|
|
except Exception as e:
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message="Error calling flowise API, please contact admin.",
|
2023-05-30 01:59:25 +00:00
|
|
|
)
|
2023-04-14 11:03:34 +00:00
|
|
|
|
2023-05-30 01:59:25 +00:00
|
|
|
# !talk command
|
|
|
|
async def talk(
|
|
|
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
|
|
|
) -> None:
|
2023-06-05 03:27:37 +00:00
|
|
|
try:
|
|
|
|
if self.pandora_data[sender_id]["conversation_id"] is not None:
|
|
|
|
data = {
|
|
|
|
"prompt": prompt,
|
|
|
|
"model": self.pandora_api_model,
|
|
|
|
"parent_message_id": self.pandora_data[sender_id][
|
|
|
|
"parent_message_id"
|
|
|
|
],
|
|
|
|
"conversation_id": self.pandora_data[sender_id]["conversation_id"],
|
|
|
|
"stream": False,
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
data = {
|
|
|
|
"prompt": prompt,
|
|
|
|
"model": self.pandora_api_model,
|
|
|
|
"parent_message_id": self.pandora_data[sender_id][
|
|
|
|
"parent_message_id"
|
|
|
|
],
|
|
|
|
"stream": False,
|
|
|
|
}
|
|
|
|
# sending typing state
|
|
|
|
await self.client.room_typing(room_id)
|
|
|
|
response = await self.pandora.talk(data)
|
|
|
|
self.pandora_data[sender_id]["conversation_id"] = response[
|
|
|
|
"conversation_id"
|
|
|
|
]
|
|
|
|
self.pandora_data[sender_id]["parent_message_id"] = response["message"][
|
|
|
|
"id"
|
|
|
|
]
|
|
|
|
content = response["message"]["content"]["parts"][0]
|
|
|
|
if self.pandora_data[sender_id]["first_time"]:
|
|
|
|
self.pandora_data[sender_id]["first_time"] = False
|
|
|
|
data = {
|
|
|
|
"model": self.pandora_api_model,
|
|
|
|
"message_id": self.pandora_data[sender_id]["parent_message_id"],
|
|
|
|
}
|
|
|
|
await self.pandora.gen_title(
|
|
|
|
data, self.pandora_data[sender_id]["conversation_id"]
|
|
|
|
)
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message=content,
|
|
|
|
reply_to_event_id="",
|
|
|
|
sender_id=sender_id,
|
|
|
|
user_message=raw_user_message,
|
|
|
|
markdown_formatted=self.markdown_formatted,
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
await send_room_message(self.client, room_id, reply_message=str(e))
|
|
|
|
|
|
|
|
# !goon command
|
|
|
|
async def goon(
|
|
|
|
self, room_id, reply_to_event_id, sender_id, raw_user_message
|
|
|
|
) -> None:
|
|
|
|
try:
|
|
|
|
# sending typing state
|
|
|
|
await self.client.room_typing(room_id)
|
2023-05-30 01:59:25 +00:00
|
|
|
data = {
|
|
|
|
"model": self.pandora_api_model,
|
|
|
|
"parent_message_id": self.pandora_data[sender_id]["parent_message_id"],
|
|
|
|
"conversation_id": self.pandora_data[sender_id]["conversation_id"],
|
|
|
|
"stream": False,
|
|
|
|
}
|
2023-06-05 03:27:37 +00:00
|
|
|
response = await self.pandora.goon(data)
|
|
|
|
self.pandora_data[sender_id]["conversation_id"] = response[
|
|
|
|
"conversation_id"
|
|
|
|
]
|
|
|
|
self.pandora_data[sender_id]["parent_message_id"] = response["message"][
|
|
|
|
"id"
|
|
|
|
]
|
|
|
|
content = response["message"]["content"]["parts"][0]
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message=content,
|
|
|
|
reply_to_event_id="",
|
|
|
|
sender_id=sender_id,
|
|
|
|
user_message=raw_user_message,
|
|
|
|
markdown_formatted=self.markdown_formatted,
|
2023-05-30 01:59:25 +00:00
|
|
|
)
|
2023-06-05 03:27:37 +00:00
|
|
|
except Exception as e:
|
|
|
|
await send_room_message(self.client, room_id, reply_message=str(e))
|
2023-04-10 17:04:06 +00:00
|
|
|
|
2023-05-30 01:59:25 +00:00
|
|
|
# !new command
|
|
|
|
async def new(
|
2023-06-05 03:27:37 +00:00
|
|
|
self,
|
|
|
|
room_id,
|
|
|
|
reply_to_event_id,
|
|
|
|
sender_id,
|
|
|
|
raw_user_message,
|
|
|
|
new_command_kind,
|
2023-05-30 01:59:25 +00:00
|
|
|
) -> None:
|
2023-06-05 03:27:37 +00:00
|
|
|
try:
|
|
|
|
if "talk" in new_command_kind:
|
|
|
|
self.pandora_session_init(sender_id)
|
|
|
|
content = (
|
|
|
|
"New conversation created, please use !talk to start chatting!"
|
|
|
|
)
|
|
|
|
elif "chat" in new_command_kind:
|
|
|
|
self.chatgpt_session_init(sender_id)
|
|
|
|
content = (
|
|
|
|
"New conversation created, please use !chat to start chatting!"
|
|
|
|
)
|
|
|
|
elif "bing" in new_command_kind:
|
|
|
|
self.bing_session_init(sender_id)
|
|
|
|
content = (
|
|
|
|
"New conversation created, please use !bing to start chatting!"
|
|
|
|
)
|
|
|
|
elif "bard" in new_command_kind:
|
|
|
|
await self.bard_session_init(sender_id)
|
|
|
|
content = (
|
|
|
|
"New conversation created, please use !bard to start chatting!"
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
content = "Unkown keyword, please use !help to see the usage!"
|
|
|
|
|
|
|
|
await send_room_message(
|
|
|
|
self.client,
|
|
|
|
room_id,
|
|
|
|
reply_message=content,
|
|
|
|
reply_to_event_id="",
|
|
|
|
sender_id=sender_id,
|
|
|
|
user_message=raw_user_message,
|
|
|
|
markdown_formatted=self.markdown_formatted,
|
|
|
|
)
|
|
|
|
except Exception as e:
|
|
|
|
await send_room_message(self.client, room_id, reply_message=str(e))
|
2023-04-10 17:04:06 +00:00
|
|
|
|
2023-05-30 01:59:25 +00:00
|
|
|
# !pic command
|
|
|
|
async def pic(self, room_id, prompt):
|
2023-06-05 03:27:37 +00:00
|
|
|
try:
|
|
|
|
await self.client.room_typing(room_id, timeout=300000)
|
|
|
|
# generate image
|
|
|
|
links = await self.imageGen.get_images(prompt)
|
|
|
|
image_path_list = await self.imageGen.save_images(
|
|
|
|
links, base_path / "images", self.output_four_images
|
|
|
|
)
|
|
|
|
# send image
|
|
|
|
for image_path in image_path_list:
|
|
|
|
await send_room_image(self.client, room_id, image_path)
|
|
|
|
await self.client.room_typing(room_id, typing_state=False)
|
|
|
|
except Exception as e:
|
|
|
|
await send_room_message(self.client, room_id, reply_message=str(e))
|
2023-03-22 14:28:22 +00:00
|
|
|
|
|
|
|
# !help command
|
|
|
|
async def help(self, room_id):
|
2023-05-30 01:59:25 +00:00
|
|
|
help_info = (
|
2023-06-05 03:27:37 +00:00
|
|
|
"!gpt [prompt], generate a one time response without context conversation\n"
|
2023-05-30 01:59:25 +00:00
|
|
|
+ "!chat [prompt], chat with context conversation\n"
|
|
|
|
+ "!bing [prompt], chat with context conversation powered by Bing AI\n"
|
|
|
|
+ "!bard [prompt], chat with Google's Bard\n"
|
|
|
|
+ "!pic [prompt], Image generation by Microsoft Bing\n"
|
|
|
|
+ "!talk [content], talk using chatgpt web (pandora)\n"
|
|
|
|
+ "!goon, continue the incomplete conversation (pandora)\n"
|
2023-06-05 03:27:37 +00:00
|
|
|
+ "!new + [chat,bing,talk,bard], start a new conversation \n"
|
2023-05-30 01:59:25 +00:00
|
|
|
+ "!lc [prompt], chat using langchain api\n"
|
|
|
|
+ "!help, help message"
|
|
|
|
) # noqa: E501
|
|
|
|
|
|
|
|
await send_room_message(self.client, room_id, reply_message=help_info)
|
2023-03-10 15:45:38 +00:00
|
|
|
|
2023-03-05 14:07:25 +00:00
|
|
|
# bot login
|
|
|
|
async def login(self) -> None:
|
2023-04-10 13:40:39 +00:00
|
|
|
if self.access_token is not None:
|
|
|
|
logger.info("Login via access_token")
|
|
|
|
else:
|
|
|
|
logger.info("Login via password")
|
|
|
|
try:
|
|
|
|
resp = await self.client.login(password=self.password)
|
|
|
|
if not isinstance(resp, LoginResponse):
|
|
|
|
logger.error("Login Failed")
|
|
|
|
sys.exit(1)
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"Error: {e}", exc_info=True)
|
2023-03-05 14:07:25 +00:00
|
|
|
|
2023-04-20 07:39:14 +00:00
|
|
|
# import keys
|
|
|
|
async def import_keys(self):
|
|
|
|
resp = await self.client.import_keys(
|
2023-05-30 01:59:25 +00:00
|
|
|
self.import_keys_path, self.import_keys_password
|
2023-04-20 07:39:14 +00:00
|
|
|
)
|
|
|
|
if isinstance(resp, EncryptionError):
|
|
|
|
logger.error(f"import_keys failed with {resp}")
|
|
|
|
else:
|
|
|
|
logger.info(
|
2023-05-30 01:59:25 +00:00
|
|
|
"import_keys success, please remove import_keys configuration!!!"
|
|
|
|
)
|
2023-04-20 07:39:14 +00:00
|
|
|
|
2023-03-05 14:07:25 +00:00
|
|
|
# sync messages in the room
|
2023-04-10 11:37:43 +00:00
|
|
|
async def sync_forever(self, timeout=30000, full_state=True) -> None:
|
2023-04-10 02:52:18 +00:00
|
|
|
await self.client.sync_forever(timeout=timeout, full_state=full_state)
|