feat: Support chatting with chatGPT web
This commit is contained in:
parent
600c5e161a
commit
c72e6d6f8e
8 changed files with 633 additions and 312 deletions
|
@ -15,4 +15,6 @@ OUTPUT_FOUR_IMAGES="true" # Optional
|
||||||
IMPORT_KEYS_PATH="element-keys.txt" # Optional
|
IMPORT_KEYS_PATH="element-keys.txt" # Optional
|
||||||
IMPORT_KEYS_PASSWORD="xxxxxxx" # Optional
|
IMPORT_KEYS_PASSWORD="xxxxxxx" # Optional
|
||||||
FLOWISE_API_URL="http://localhost:3000/api/v1/prediction/xxxx" # Optional
|
FLOWISE_API_URL="http://localhost:3000/api/v1/prediction/xxxx" # Optional
|
||||||
FLOWISE_API_KEY="xxxxxxxxxxxxxxxxxxxxxxx" # Optional
|
FLOWISE_API_KEY="xxxxxxxxxxxxxxxxxxxxxxx" # Optional
|
||||||
|
PANDORA_API_ENDPOINT="http://pandora:8008" # Optional
|
||||||
|
PANDORA_API_MODEL="text-davinci-002-render-sha-mobile" # Optional
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"python.analysis.typeCheckingMode": "off"
|
|
||||||
}
|
|
12
README.md
12
README.md
|
@ -1,16 +1,18 @@
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This is a simple Matrix bot that uses OpenAI's GPT API and Bing AI and Google Bard to generate responses to user inputs. The bot responds to six types of prompts: `!gpt`, `!chat` and `!bing` and `!pic` and `!bard` and `!lc` depending on the first word of the prompt.
|
This is a simple Matrix bot that uses OpenAI's GPT API and Bing AI and Google Bard to generate responses to user inputs. The bot responds to these commands: `!gpt`, `!chat` and `!bing` and `!pic` and `!bard` and `!talk`, `!goon`, `!new` and `!lc` and `!help` depending on the first word of the prompt.
|
||||||
![Bing](https://user-images.githubusercontent.com/32976627/231073146-3e380217-a6a2-413d-9203-ab36965b909d.png)
|
![Bing](https://user-images.githubusercontent.com/32976627/231073146-3e380217-a6a2-413d-9203-ab36965b909d.png)
|
||||||
![image](https://user-images.githubusercontent.com/32976627/232036790-e830145c-914e-40be-b3e6-c02cba93329c.png)
|
![image](https://user-images.githubusercontent.com/32976627/232036790-e830145c-914e-40be-b3e6-c02cba93329c.png)
|
||||||
![ChatGPT](https://i.imgur.com/kK4rnPf.jpeg)
|
![ChatGPT](https://i.imgur.com/kK4rnPf.jpeg)
|
||||||
|
|
||||||
## Feature
|
## Feature
|
||||||
|
|
||||||
1. Support Openai ChatGPT and Bing AI and Google Bard and Langchain([Flowise](https://github.com/FlowiseAI/Flowise))
|
1. Support Openai ChatGPT and Bing AI and Google Bard
|
||||||
2. Support Bing Image Creator
|
2. Support Bing Image Creator
|
||||||
3. Support E2E Encrypted Room
|
3. Support E2E Encrypted Room
|
||||||
4. Colorful code blocks
|
4. Colorful code blocks
|
||||||
|
5. Langchain([Flowise](https://github.com/FlowiseAI/Flowise))
|
||||||
|
6. ChatGPT Web ([pandora](https://github.com/pengzhile/pandora) with Session isolation support)
|
||||||
|
|
||||||
## Installation and Setup
|
## Installation and Setup
|
||||||
|
|
||||||
|
@ -109,6 +111,12 @@ To interact with the bot, simply send a message to the bot in the Matrix room wi
|
||||||
!pic A bridal bouquet made of succulents
|
!pic A bridal bouquet made of succulents
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The following commands need pandora http api:
|
||||||
|
https://github.com/pengzhile/pandora/blob/master/doc/wiki_en.md#http-restful-api
|
||||||
|
- `!talk + [prompt]` Chat using chatGPT web with context conversation
|
||||||
|
- `!goon` Ask chatGPT to complete the missing part from previous conversation
|
||||||
|
- `!new` Start a new converstaion
|
||||||
|
|
||||||
## Bing AI and Image Generation
|
## Bing AI and Image Generation
|
||||||
|
|
||||||
|
|
||||||
|
|
695
bot.py
695
bot.py
|
@ -4,13 +4,27 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Union, Optional
|
from typing import Union, Optional
|
||||||
|
import uuid
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from nio import (AsyncClient, AsyncClientConfig, InviteMemberEvent, JoinError,
|
from nio import (
|
||||||
KeyVerificationCancel, KeyVerificationEvent, EncryptionError,
|
AsyncClient,
|
||||||
KeyVerificationKey, KeyVerificationMac, KeyVerificationStart,
|
AsyncClientConfig,
|
||||||
LocalProtocolError, LoginResponse, MatrixRoom, MegolmEvent,
|
InviteMemberEvent,
|
||||||
RoomMessageText, ToDeviceError)
|
JoinError,
|
||||||
|
KeyVerificationCancel,
|
||||||
|
KeyVerificationEvent,
|
||||||
|
EncryptionError,
|
||||||
|
KeyVerificationKey,
|
||||||
|
KeyVerificationMac,
|
||||||
|
KeyVerificationStart,
|
||||||
|
LocalProtocolError,
|
||||||
|
LoginResponse,
|
||||||
|
MatrixRoom,
|
||||||
|
MegolmEvent,
|
||||||
|
RoomMessageText,
|
||||||
|
ToDeviceError,
|
||||||
|
)
|
||||||
from nio.store.database import SqliteStore
|
from nio.store.database import SqliteStore
|
||||||
|
|
||||||
from askgpt import askGPT
|
from askgpt import askGPT
|
||||||
|
@ -22,6 +36,7 @@ from send_message import send_room_message
|
||||||
from v3 import Chatbot
|
from v3 import Chatbot
|
||||||
from bard import Bardbot
|
from bard import Bardbot
|
||||||
from flowise import flowise_query
|
from flowise import flowise_query
|
||||||
|
from pandora import Pandora
|
||||||
|
|
||||||
logger = getlogger()
|
logger = getlogger()
|
||||||
|
|
||||||
|
@ -32,8 +47,8 @@ class Bot:
|
||||||
homeserver: str,
|
homeserver: str,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
device_id: str,
|
device_id: str,
|
||||||
chatgpt_api_endpoint: str = os.environ.get(
|
chatgpt_api_endpoint: str = os.environ.get("CHATGPT_API_ENDPOINT")
|
||||||
"CHATGPT_API_ENDPOINT") or "https://api.openai.com/v1/chat/completions",
|
or "https://api.openai.com/v1/chat/completions",
|
||||||
api_key: Union[str, None] = None,
|
api_key: Union[str, None] = None,
|
||||||
room_id: Union[str, None] = None,
|
room_id: Union[str, None] = None,
|
||||||
bing_api_endpoint: Union[str, None] = None,
|
bing_api_endpoint: Union[str, None] = None,
|
||||||
|
@ -41,20 +56,21 @@ class Bot:
|
||||||
access_token: Union[str, None] = None,
|
access_token: Union[str, None] = None,
|
||||||
bard_token: Union[str, None] = None,
|
bard_token: Union[str, None] = None,
|
||||||
jailbreakEnabled: Union[bool, None] = True,
|
jailbreakEnabled: Union[bool, None] = True,
|
||||||
bing_auth_cookie: Union[str, None] = '',
|
bing_auth_cookie: Union[str, None] = "",
|
||||||
markdown_formatted: Union[bool, None] = False,
|
markdown_formatted: Union[bool, None] = False,
|
||||||
output_four_images: Union[bool, None] = False,
|
output_four_images: Union[bool, None] = False,
|
||||||
import_keys_path: Optional[str] = None,
|
import_keys_path: Optional[str] = None,
|
||||||
import_keys_password: Optional[str] = None,
|
import_keys_password: Optional[str] = None,
|
||||||
flowise_api_url: Optional[str] = None,
|
flowise_api_url: Optional[str] = None,
|
||||||
flowise_api_key: Optional[str] = None
|
flowise_api_key: Optional[str] = None,
|
||||||
|
pandora_api_endpoint: Optional[str] = None,
|
||||||
|
pandora_api_model: Optional[str] = None,
|
||||||
):
|
):
|
||||||
if (homeserver is None or user_id is None
|
if homeserver is None or user_id is None or device_id is None:
|
||||||
or device_id is None):
|
|
||||||
logger.warning("homeserver && user_id && device_id is required")
|
logger.warning("homeserver && user_id && device_id is required")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if (password is None and access_token is None):
|
if password is None and access_token is None:
|
||||||
logger.warning("password or access_toekn is required")
|
logger.warning("password or access_toekn is required")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
@ -75,7 +91,7 @@ class Bot:
|
||||||
self.session = aiohttp.ClientSession()
|
self.session = aiohttp.ClientSession()
|
||||||
|
|
||||||
if bing_api_endpoint is None:
|
if bing_api_endpoint is None:
|
||||||
self.bing_api_endpoint = ''
|
self.bing_api_endpoint = ""
|
||||||
else:
|
else:
|
||||||
self.bing_api_endpoint = bing_api_endpoint
|
self.bing_api_endpoint = bing_api_endpoint
|
||||||
|
|
||||||
|
@ -85,7 +101,7 @@ class Bot:
|
||||||
self.jailbreakEnabled = jailbreakEnabled
|
self.jailbreakEnabled = jailbreakEnabled
|
||||||
|
|
||||||
if bing_auth_cookie is None:
|
if bing_auth_cookie is None:
|
||||||
self.bing_auth_cookie = ''
|
self.bing_auth_cookie = ""
|
||||||
else:
|
else:
|
||||||
self.bing_auth_cookie = bing_auth_cookie
|
self.bing_auth_cookie = bing_auth_cookie
|
||||||
|
|
||||||
|
@ -101,27 +117,30 @@ class Bot:
|
||||||
|
|
||||||
# initialize AsyncClient object
|
# initialize AsyncClient object
|
||||||
self.store_path = os.getcwd()
|
self.store_path = os.getcwd()
|
||||||
self.config = AsyncClientConfig(store=SqliteStore,
|
self.config = AsyncClientConfig(
|
||||||
store_name="db",
|
store=SqliteStore,
|
||||||
store_sync_tokens=True,
|
store_name="db",
|
||||||
encryption_enabled=True,
|
store_sync_tokens=True,
|
||||||
)
|
encryption_enabled=True,
|
||||||
self.client = AsyncClient(homeserver=self.homeserver, user=self.user_id,
|
)
|
||||||
device_id=self.device_id,
|
self.client = AsyncClient(
|
||||||
config=self.config, store_path=self.store_path,)
|
homeserver=self.homeserver,
|
||||||
|
user=self.user_id,
|
||||||
|
device_id=self.device_id,
|
||||||
|
config=self.config,
|
||||||
|
store_path=self.store_path,
|
||||||
|
)
|
||||||
|
|
||||||
if self.access_token is not None:
|
if self.access_token is not None:
|
||||||
self.client.access_token = self.access_token
|
self.client.access_token = self.access_token
|
||||||
|
|
||||||
# setup event callbacks
|
# setup event callbacks
|
||||||
self.client.add_event_callback(
|
self.client.add_event_callback(self.message_callback, (RoomMessageText,))
|
||||||
self.message_callback, (RoomMessageText, ))
|
self.client.add_event_callback(self.decryption_failure, (MegolmEvent,))
|
||||||
self.client.add_event_callback(
|
self.client.add_event_callback(self.invite_callback, (InviteMemberEvent,))
|
||||||
self.decryption_failure, (MegolmEvent, ))
|
|
||||||
self.client.add_event_callback(
|
|
||||||
self.invite_callback, (InviteMemberEvent, ))
|
|
||||||
self.client.add_to_device_callback(
|
self.client.add_to_device_callback(
|
||||||
self.to_device_callback, (KeyVerificationEvent, ))
|
self.to_device_callback, (KeyVerificationEvent,)
|
||||||
|
)
|
||||||
|
|
||||||
# regular expression to match keyword commands
|
# regular expression to match keyword commands
|
||||||
self.gpt_prog = re.compile(r"^\s*!gpt\s*(.+)$")
|
self.gpt_prog = re.compile(r"^\s*!gpt\s*(.+)$")
|
||||||
|
@ -131,6 +150,9 @@ class Bot:
|
||||||
self.pic_prog = re.compile(r"^\s*!pic\s*(.+)$")
|
self.pic_prog = re.compile(r"^\s*!pic\s*(.+)$")
|
||||||
self.lc_prog = re.compile(r"^\s*!lc\s*(.+)$")
|
self.lc_prog = re.compile(r"^\s*!lc\s*(.+)$")
|
||||||
self.help_prog = re.compile(r"^\s*!help\s*.*$")
|
self.help_prog = re.compile(r"^\s*!help\s*.*$")
|
||||||
|
self.talk_prog = re.compile(r"^\s*!talk\s*(.+)$")
|
||||||
|
self.goon_prog = re.compile(r"^\s*!goon\s*.*$")
|
||||||
|
self.new_prog = re.compile(r"^\s*!new\s*.*$")
|
||||||
|
|
||||||
# initialize chatbot and chatgpt_api_endpoint
|
# initialize chatbot and chatgpt_api_endpoint
|
||||||
if self.api_key is not None:
|
if self.api_key is not None:
|
||||||
|
@ -147,18 +169,32 @@ class Bot:
|
||||||
self.askgpt = askGPT(self.session)
|
self.askgpt = askGPT(self.session)
|
||||||
|
|
||||||
# initialize bingbot
|
# initialize bingbot
|
||||||
if self.bing_api_endpoint != '':
|
if self.bing_api_endpoint != "":
|
||||||
self.bingbot = BingBot(
|
self.bingbot = BingBot(
|
||||||
self.session, bing_api_endpoint, jailbreakEnabled=self.jailbreakEnabled)
|
self.session, bing_api_endpoint, jailbreakEnabled=self.jailbreakEnabled
|
||||||
|
)
|
||||||
|
|
||||||
# initialize BingImageGenAsync
|
# initialize BingImageGenAsync
|
||||||
if self.bing_auth_cookie != '':
|
if self.bing_auth_cookie != "":
|
||||||
self.imageGen = ImageGenAsync(self.bing_auth_cookie, quiet=True)
|
self.imageGen = ImageGenAsync(self.bing_auth_cookie, quiet=True)
|
||||||
|
|
||||||
# initialize Bardbot
|
# initialize Bardbot
|
||||||
if bard_token is not None:
|
if bard_token is not None:
|
||||||
self.bardbot = Bardbot(self.bard_token)
|
self.bardbot = Bardbot(self.bard_token)
|
||||||
|
|
||||||
|
# initialize pandora
|
||||||
|
if pandora_api_endpoint is not None:
|
||||||
|
self.pandora_api_endpoint = pandora_api_endpoint
|
||||||
|
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
|
||||||
|
|
||||||
|
self.pandora_data = {}
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
try:
|
try:
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
|
@ -170,6 +206,13 @@ class Bot:
|
||||||
async def _close(self):
|
async def _close(self):
|
||||||
await self.session.close()
|
await self.session.close()
|
||||||
|
|
||||||
|
def pandora_init(self, sender_id: str) -> None:
|
||||||
|
self.pandora_data[sender_id] = {
|
||||||
|
"conversation_id": None,
|
||||||
|
"parent_message_id": str(uuid.uuid4()),
|
||||||
|
"first_time": True,
|
||||||
|
}
|
||||||
|
|
||||||
# message_callback RoomMessageText event
|
# message_callback RoomMessageText event
|
||||||
async def message_callback(self, room: MatrixRoom, event: RoomMessageText) -> None:
|
async def message_callback(self, room: MatrixRoom, event: RoomMessageText) -> None:
|
||||||
if self.room_id is None:
|
if self.room_id is None:
|
||||||
|
@ -186,6 +229,9 @@ class Bot:
|
||||||
# sender_id
|
# sender_id
|
||||||
sender_id = event.sender
|
sender_id = event.sender
|
||||||
|
|
||||||
|
if sender_id not in self.pandora_data:
|
||||||
|
self.pandora_init(sender_id)
|
||||||
|
|
||||||
# user_message
|
# user_message
|
||||||
raw_user_message = event.body
|
raw_user_message = event.body
|
||||||
|
|
||||||
|
@ -206,59 +252,67 @@ class Bot:
|
||||||
prompt = n.group(1)
|
prompt = n.group(1)
|
||||||
if self.api_key is not None:
|
if self.api_key is not None:
|
||||||
try:
|
try:
|
||||||
asyncio.create_task(self.chat(room_id,
|
asyncio.create_task(
|
||||||
reply_to_event_id,
|
self.chat(
|
||||||
prompt,
|
room_id,
|
||||||
sender_id,
|
reply_to_event_id,
|
||||||
raw_user_message
|
prompt,
|
||||||
)
|
sender_id,
|
||||||
)
|
raw_user_message,
|
||||||
|
)
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e, exc_info=True)
|
logger.error(e, exc_info=True)
|
||||||
await send_room_message(self.client, room_id,
|
await send_room_message(
|
||||||
reply_message=str(e))
|
self.client, room_id, reply_message=str(e)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.warning("No API_KEY provided")
|
logger.warning("No API_KEY provided")
|
||||||
await send_room_message(self.client, room_id,
|
await send_room_message(
|
||||||
reply_message="API_KEY not provided")
|
self.client, room_id, reply_message="API_KEY not provided"
|
||||||
|
)
|
||||||
|
|
||||||
m = self.gpt_prog.match(content_body)
|
m = self.gpt_prog.match(content_body)
|
||||||
if m:
|
if m:
|
||||||
prompt = m.group(1)
|
prompt = m.group(1)
|
||||||
try:
|
try:
|
||||||
asyncio.create_task(self.gpt(
|
asyncio.create_task(
|
||||||
room_id,
|
self.gpt(
|
||||||
reply_to_event_id,
|
room_id,
|
||||||
prompt, sender_id,
|
reply_to_event_id,
|
||||||
raw_user_message
|
prompt,
|
||||||
)
|
sender_id,
|
||||||
|
raw_user_message,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e, exc_info=True)
|
logger.error(e, exc_info=True)
|
||||||
await send_room_message(self.client, room_id, reply_message=str(e))
|
await send_room_message(self.client, room_id, reply_message=str(e))
|
||||||
|
|
||||||
# bing ai
|
# bing ai
|
||||||
if self.bing_api_endpoint != '':
|
if self.bing_api_endpoint != "":
|
||||||
b = self.bing_prog.match(content_body)
|
b = self.bing_prog.match(content_body)
|
||||||
if b:
|
if b:
|
||||||
prompt = b.group(1)
|
prompt = b.group(1)
|
||||||
# raw_content_body used for construct formatted_body
|
# raw_content_body used for construct formatted_body
|
||||||
try:
|
try:
|
||||||
asyncio.create_task(self.bing(
|
asyncio.create_task(
|
||||||
room_id,
|
self.bing(
|
||||||
reply_to_event_id,
|
room_id,
|
||||||
prompt,
|
reply_to_event_id,
|
||||||
sender_id,
|
prompt,
|
||||||
raw_user_message
|
sender_id,
|
||||||
)
|
raw_user_message,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e, exc_info=True)
|
logger.error(e, exc_info=True)
|
||||||
await send_room_message(self.client, room_id,
|
await send_room_message(
|
||||||
reply_message=str(e))
|
self.client, room_id, reply_message=str(e)
|
||||||
|
)
|
||||||
|
|
||||||
# Image Generation by Microsoft Bing
|
# Image Generation by Microsoft Bing
|
||||||
if self.bing_auth_cookie != '':
|
if self.bing_auth_cookie != "":
|
||||||
i = self.pic_prog.match(content_body)
|
i = self.pic_prog.match(content_body)
|
||||||
if i:
|
if i:
|
||||||
prompt = i.group(1)
|
prompt = i.group(1)
|
||||||
|
@ -266,8 +320,9 @@ class Bot:
|
||||||
asyncio.create_task(self.pic(room_id, prompt))
|
asyncio.create_task(self.pic(room_id, prompt))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e, exc_info=True)
|
logger.error(e, exc_info=True)
|
||||||
await send_room_message(self.client, room_id,
|
await send_room_message(
|
||||||
reply_message=str(e))
|
self.client, room_id, reply_message=str(e)
|
||||||
|
)
|
||||||
|
|
||||||
# Google's Bard
|
# Google's Bard
|
||||||
if self.bard_token is not None:
|
if self.bard_token is not None:
|
||||||
|
@ -275,13 +330,14 @@ class Bot:
|
||||||
if b:
|
if b:
|
||||||
prompt = b.group(1)
|
prompt = b.group(1)
|
||||||
try:
|
try:
|
||||||
asyncio.create_task(self.bard(
|
asyncio.create_task(
|
||||||
room_id,
|
self.bard(
|
||||||
reply_to_event_id,
|
room_id,
|
||||||
prompt,
|
reply_to_event_id,
|
||||||
sender_id,
|
prompt,
|
||||||
raw_user_message
|
sender_id,
|
||||||
)
|
raw_user_message,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e, exc_info=True)
|
logger.error(e, exc_info=True)
|
||||||
|
@ -293,13 +349,63 @@ class Bot:
|
||||||
if m:
|
if m:
|
||||||
prompt = m.group(1)
|
prompt = m.group(1)
|
||||||
try:
|
try:
|
||||||
asyncio.create_task(self.lc(
|
asyncio.create_task(
|
||||||
room_id,
|
self.lc(
|
||||||
reply_to_event_id,
|
room_id,
|
||||||
prompt,
|
reply_to_event_id,
|
||||||
sender_id,
|
prompt,
|
||||||
raw_user_message
|
sender_id,
|
||||||
|
raw_user_message,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e, exc_info=True)
|
||||||
|
await send_room_message(self.client, room_id, reply_message={e})
|
||||||
|
|
||||||
|
# pandora
|
||||||
|
if self.pandora_api_endpoint is not None:
|
||||||
|
t = self.talk_prog.match(content_body)
|
||||||
|
if t:
|
||||||
|
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)
|
||||||
|
await send_room_message(self.client, room_id, reply_message={e})
|
||||||
|
|
||||||
|
g = self.goon_prog.match(content_body)
|
||||||
|
if g:
|
||||||
|
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)
|
||||||
|
await send_room_message(self.client, room_id, reply_message={e})
|
||||||
|
|
||||||
|
n = self.new_prog.match(content_body)
|
||||||
|
if n:
|
||||||
|
try:
|
||||||
|
asyncio.create_task(
|
||||||
|
self.new(
|
||||||
|
room_id,
|
||||||
|
reply_to_event_id,
|
||||||
|
sender_id,
|
||||||
|
raw_user_message,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e, exc_info=True)
|
logger.error(e, exc_info=True)
|
||||||
|
@ -317,8 +423,8 @@ class Bot:
|
||||||
|
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Failed to decrypt message: {event.event_id} \
|
f"Failed to decrypt message: {event.event_id} \
|
||||||
from {event.sender} in {room.room_id}\n" +
|
from {event.sender} in {room.room_id}\n"
|
||||||
"Please make sure the bot current session is verified"
|
+ "Please make sure the bot current session is verified"
|
||||||
)
|
)
|
||||||
|
|
||||||
# invite_callback event
|
# invite_callback event
|
||||||
|
@ -334,7 +440,8 @@ class Bot:
|
||||||
if type(result) == JoinError:
|
if type(result) == JoinError:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Error joining room {room.room_id} (attempt %d): %s",
|
f"Error joining room {room.room_id} (attempt %d): %s",
|
||||||
attempt, result.message,
|
attempt,
|
||||||
|
result.message,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
@ -356,11 +463,11 @@ class Bot:
|
||||||
try:
|
try:
|
||||||
client = self.client
|
client = self.client
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Device Event of type {type(event)} received in "
|
f"Device Event of type {type(event)} received in " "to_device_cb()."
|
||||||
"to_device_cb().")
|
)
|
||||||
|
|
||||||
if isinstance(event, KeyVerificationStart): # first step
|
if isinstance(event, KeyVerificationStart): # first step
|
||||||
""" first step: receive KeyVerificationStart
|
"""first step: receive KeyVerificationStart
|
||||||
KeyVerificationStart(
|
KeyVerificationStart(
|
||||||
source={'content':
|
source={'content':
|
||||||
{'method': 'm.sas.v1',
|
{'method': 'm.sas.v1',
|
||||||
|
@ -390,12 +497,13 @@ class Bot:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if "emoji" not in event.short_authentication_string:
|
if "emoji" not in event.short_authentication_string:
|
||||||
estr = ("Other device does not support emoji verification "
|
estr = (
|
||||||
f"{event.short_authentication_string}. Aborting.")
|
"Other device does not support emoji verification "
|
||||||
|
f"{event.short_authentication_string}. Aborting."
|
||||||
|
)
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
return
|
return
|
||||||
resp = await client.accept_key_verification(
|
resp = await client.accept_key_verification(event.transaction_id)
|
||||||
event.transaction_id)
|
|
||||||
if isinstance(resp, ToDeviceError):
|
if isinstance(resp, ToDeviceError):
|
||||||
estr = f"accept_key_verification() failed with {resp}"
|
estr = f"accept_key_verification() failed with {resp}"
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
|
@ -409,7 +517,7 @@ class Bot:
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
|
|
||||||
elif isinstance(event, KeyVerificationCancel): # anytime
|
elif isinstance(event, KeyVerificationCancel): # anytime
|
||||||
""" at any time: receive KeyVerificationCancel
|
"""at any time: receive KeyVerificationCancel
|
||||||
KeyVerificationCancel(source={
|
KeyVerificationCancel(source={
|
||||||
'content': {'code': 'm.mismatched_sas',
|
'content': {'code': 'm.mismatched_sas',
|
||||||
'reason': 'Mismatched authentication string',
|
'reason': 'Mismatched authentication string',
|
||||||
|
@ -426,12 +534,14 @@ class Bot:
|
||||||
# client.cancel_key_verification(tx_id, reject=False)
|
# client.cancel_key_verification(tx_id, reject=False)
|
||||||
# here. The SAS flow is already cancelled.
|
# here. The SAS flow is already cancelled.
|
||||||
# We only need to inform the user.
|
# We only need to inform the user.
|
||||||
estr = (f"Verification has been cancelled by {event.sender} "
|
estr = (
|
||||||
f"for reason \"{event.reason}\".")
|
f"Verification has been cancelled by {event.sender} "
|
||||||
|
f'for reason "{event.reason}".'
|
||||||
|
)
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
|
|
||||||
elif isinstance(event, KeyVerificationKey): # second step
|
elif isinstance(event, KeyVerificationKey): # second step
|
||||||
""" Second step is to receive KeyVerificationKey
|
"""Second step is to receive KeyVerificationKey
|
||||||
KeyVerificationKey(
|
KeyVerificationKey(
|
||||||
source={'content': {
|
source={'content': {
|
||||||
'key': 'SomeCryptoKey',
|
'key': 'SomeCryptoKey',
|
||||||
|
@ -456,36 +566,38 @@ class Bot:
|
||||||
# automatic match, so we use y
|
# automatic match, so we use y
|
||||||
yn = "y"
|
yn = "y"
|
||||||
if yn.lower() == "y":
|
if yn.lower() == "y":
|
||||||
estr = ("Match! The verification for this "
|
estr = (
|
||||||
"device will be accepted.")
|
"Match! The verification for this " "device will be accepted."
|
||||||
|
)
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
resp = await client.confirm_short_auth_string(
|
resp = await client.confirm_short_auth_string(event.transaction_id)
|
||||||
event.transaction_id)
|
|
||||||
if isinstance(resp, ToDeviceError):
|
if isinstance(resp, ToDeviceError):
|
||||||
estr = ("confirm_short_auth_string() "
|
estr = "confirm_short_auth_string() " f"failed with {resp}"
|
||||||
f"failed with {resp}")
|
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
elif yn.lower() == "n": # no, don't match, reject
|
elif yn.lower() == "n": # no, don't match, reject
|
||||||
estr = ("No match! Device will NOT be verified "
|
estr = (
|
||||||
"by rejecting verification.")
|
"No match! Device will NOT be verified "
|
||||||
|
"by rejecting verification."
|
||||||
|
)
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
resp = await client.cancel_key_verification(
|
resp = await client.cancel_key_verification(
|
||||||
event.transaction_id, reject=True)
|
event.transaction_id, reject=True
|
||||||
|
)
|
||||||
if isinstance(resp, ToDeviceError):
|
if isinstance(resp, ToDeviceError):
|
||||||
estr = (f"cancel_key_verification failed with {resp}")
|
estr = f"cancel_key_verification failed with {resp}"
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
else: # C or anything for cancel
|
else: # C or anything for cancel
|
||||||
estr = ("Cancelled by user! Verification will be "
|
estr = "Cancelled by user! Verification will be " "cancelled."
|
||||||
"cancelled.")
|
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
resp = await client.cancel_key_verification(
|
resp = await client.cancel_key_verification(
|
||||||
event.transaction_id, reject=False)
|
event.transaction_id, reject=False
|
||||||
|
)
|
||||||
if isinstance(resp, ToDeviceError):
|
if isinstance(resp, ToDeviceError):
|
||||||
estr = (f"cancel_key_verification failed with {resp}")
|
estr = f"cancel_key_verification failed with {resp}"
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
|
|
||||||
elif isinstance(event, KeyVerificationMac): # third step
|
elif isinstance(event, KeyVerificationMac): # third step
|
||||||
""" Third step is to receive KeyVerificationMac
|
"""Third step is to receive KeyVerificationMac
|
||||||
KeyVerificationMac(
|
KeyVerificationMac(
|
||||||
source={'content': {
|
source={'content': {
|
||||||
'mac': {'ed25519:DEVICEIDXY': 'SomeKey1',
|
'mac': {'ed25519:DEVICEIDXY': 'SomeKey1',
|
||||||
|
@ -505,185 +617,265 @@ class Bot:
|
||||||
todevice_msg = sas.get_mac()
|
todevice_msg = sas.get_mac()
|
||||||
except LocalProtocolError as e:
|
except LocalProtocolError as e:
|
||||||
# e.g. it might have been cancelled by ourselves
|
# e.g. it might have been cancelled by ourselves
|
||||||
estr = (f"Cancelled or protocol error: Reason: {e}.\n"
|
estr = (
|
||||||
f"Verification with {event.sender} not concluded. "
|
f"Cancelled or protocol error: Reason: {e}.\n"
|
||||||
"Try again?")
|
f"Verification with {event.sender} not concluded. "
|
||||||
|
"Try again?"
|
||||||
|
)
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
else:
|
else:
|
||||||
resp = await client.to_device(todevice_msg)
|
resp = await client.to_device(todevice_msg)
|
||||||
if isinstance(resp, ToDeviceError):
|
if isinstance(resp, ToDeviceError):
|
||||||
estr = f"to_device failed with {resp}"
|
estr = f"to_device failed with {resp}"
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
estr = (f"sas.we_started_it = {sas.we_started_it}\n"
|
estr = (
|
||||||
f"sas.sas_accepted = {sas.sas_accepted}\n"
|
f"sas.we_started_it = {sas.we_started_it}\n"
|
||||||
f"sas.canceled = {sas.canceled}\n"
|
f"sas.sas_accepted = {sas.sas_accepted}\n"
|
||||||
f"sas.timed_out = {sas.timed_out}\n"
|
f"sas.canceled = {sas.canceled}\n"
|
||||||
f"sas.verified = {sas.verified}\n"
|
f"sas.timed_out = {sas.timed_out}\n"
|
||||||
f"sas.verified_devices = {sas.verified_devices}\n")
|
f"sas.verified = {sas.verified}\n"
|
||||||
|
f"sas.verified_devices = {sas.verified_devices}\n"
|
||||||
|
)
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
estr = ("Emoji verification was successful!\n"
|
estr = (
|
||||||
"Initiate another Emoji verification from "
|
"Emoji verification was successful!\n"
|
||||||
"another device or room if desired. "
|
"Initiate another Emoji verification from "
|
||||||
"Or if done verifying, hit Control-C to stop the "
|
"another device or room if desired. "
|
||||||
"bot in order to restart it as a service or to "
|
"Or if done verifying, hit Control-C to stop the "
|
||||||
"run it in the background.")
|
"bot in order to restart it as a service or to "
|
||||||
|
"run it in the background."
|
||||||
|
)
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
else:
|
else:
|
||||||
estr = (f"Received unexpected event type {type(event)}. "
|
estr = (
|
||||||
f"Event is {event}. Event will be ignored.")
|
f"Received unexpected event type {type(event)}. "
|
||||||
|
f"Event is {event}. Event will be ignored."
|
||||||
|
)
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
except BaseException:
|
except BaseException:
|
||||||
estr = traceback.format_exc()
|
estr = traceback.format_exc()
|
||||||
logger.info(estr)
|
logger.info(estr)
|
||||||
|
|
||||||
# !chat command
|
# !chat command
|
||||||
async def chat(self, room_id, reply_to_event_id, prompt, sender_id,
|
async def chat(
|
||||||
raw_user_message):
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
||||||
|
):
|
||||||
await self.client.room_typing(room_id, timeout=120000)
|
await self.client.room_typing(room_id, timeout=120000)
|
||||||
try:
|
|
||||||
text = await self.chatbot.ask_async(prompt)
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(e)
|
|
||||||
|
|
||||||
try:
|
text = await self.chatbot.ask_async(prompt)
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
await send_room_message(self.client, room_id, reply_message=text,
|
await send_room_message(
|
||||||
reply_to_event_id="", sender_id=sender_id,
|
self.client,
|
||||||
user_message=raw_user_message,
|
room_id,
|
||||||
markdown_formatted=self.markdown_formatted)
|
reply_message=text,
|
||||||
except Exception as e:
|
reply_to_event_id="",
|
||||||
logger.error(f"Error: {e}", exc_info=True)
|
sender_id=sender_id,
|
||||||
|
user_message=raw_user_message,
|
||||||
|
markdown_formatted=self.markdown_formatted,
|
||||||
|
)
|
||||||
|
|
||||||
# !gpt command
|
# !gpt command
|
||||||
async def gpt(self, room_id, reply_to_event_id, prompt, sender_id,
|
async def gpt(
|
||||||
raw_user_message) -> None:
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
||||||
try:
|
) -> None:
|
||||||
# sending typing state
|
# sending typing state
|
||||||
await self.client.room_typing(room_id, timeout=240000)
|
await self.client.room_typing(room_id, timeout=240000)
|
||||||
# timeout 240s
|
# timeout 240s
|
||||||
text = await asyncio.wait_for(self.askgpt.oneTimeAsk(prompt,
|
text = await asyncio.wait_for(
|
||||||
self.chatgpt_api_endpoint,
|
self.askgpt.oneTimeAsk(prompt, self.chatgpt_api_endpoint, self.headers),
|
||||||
self.headers),
|
timeout=240,
|
||||||
timeout=240)
|
)
|
||||||
except TimeoutError:
|
|
||||||
logger.error("TimeoutException", exc_info=True)
|
|
||||||
raise Exception("Timeout error")
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(e)
|
|
||||||
|
|
||||||
try:
|
text = text.strip()
|
||||||
text = text.strip()
|
await send_room_message(
|
||||||
await send_room_message(self.client, room_id, reply_message=text,
|
self.client,
|
||||||
reply_to_event_id="", sender_id=sender_id,
|
room_id,
|
||||||
user_message=raw_user_message,
|
reply_message=text,
|
||||||
markdown_formatted=self.markdown_formatted)
|
reply_to_event_id="",
|
||||||
except Exception as e:
|
sender_id=sender_id,
|
||||||
logger.error(f"Error: {e}", exc_info=True)
|
user_message=raw_user_message,
|
||||||
|
markdown_formatted=self.markdown_formatted,
|
||||||
|
)
|
||||||
|
|
||||||
# !bing command
|
# !bing command
|
||||||
async def bing(self, room_id, reply_to_event_id, prompt, sender_id,
|
async def bing(
|
||||||
raw_user_message) -> None:
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
||||||
try:
|
) -> None:
|
||||||
# sending typing state
|
# sending typing state
|
||||||
await self.client.room_typing(room_id, timeout=180000)
|
await self.client.room_typing(room_id, timeout=180000)
|
||||||
# timeout 240s
|
# timeout 240s
|
||||||
text = await asyncio.wait_for(self.bingbot.ask_bing(prompt), timeout=240)
|
text = await asyncio.wait_for(self.bingbot.ask_bing(prompt), timeout=240)
|
||||||
except TimeoutError:
|
|
||||||
logger.error("timeoutException", exc_info=True)
|
|
||||||
raise Exception("Timeout error")
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(e)
|
|
||||||
|
|
||||||
try:
|
text = text.strip()
|
||||||
text = text.strip()
|
await send_room_message(
|
||||||
await send_room_message(self.client, room_id, reply_message=text,
|
self.client,
|
||||||
reply_to_event_id="", sender_id=sender_id,
|
room_id,
|
||||||
user_message=raw_user_message,
|
reply_message=text,
|
||||||
markdown_formatted=self.markdown_formatted)
|
reply_to_event_id="",
|
||||||
except Exception as e:
|
sender_id=sender_id,
|
||||||
logger.error(e, exc_info=True)
|
user_message=raw_user_message,
|
||||||
|
markdown_formatted=self.markdown_formatted,
|
||||||
|
)
|
||||||
|
|
||||||
# !bard command
|
# !bard command
|
||||||
async def bard(self, room_id, reply_to_event_id, prompt, sender_id,
|
async def bard(
|
||||||
raw_user_message) -> None:
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
||||||
try:
|
) -> None:
|
||||||
# sending typing state
|
# sending typing state
|
||||||
await self.client.room_typing(room_id)
|
await self.client.room_typing(room_id)
|
||||||
response = await asyncio.to_thread(self.bardbot.ask, prompt)
|
response = await asyncio.to_thread(self.bardbot.ask, prompt)
|
||||||
except Exception as e:
|
|
||||||
raise Exception(e)
|
|
||||||
|
|
||||||
try:
|
content = str(response["content"]).strip()
|
||||||
content = str(response['content']).strip()
|
await send_room_message(
|
||||||
await send_room_message(self.client, room_id, reply_message=content,
|
self.client,
|
||||||
reply_to_event_id="", sender_id=sender_id,
|
room_id,
|
||||||
user_message=raw_user_message,
|
reply_message=content,
|
||||||
markdown_formatted=self.markdown_formatted)
|
reply_to_event_id="",
|
||||||
except Exception as e:
|
sender_id=sender_id,
|
||||||
logger.error(e, exc_info=True)
|
user_message=raw_user_message,
|
||||||
|
markdown_formatted=self.markdown_formatted,
|
||||||
|
)
|
||||||
|
|
||||||
# !lc command
|
# !lc command
|
||||||
async def lc(self, room_id, reply_to_event_id, prompt, sender_id,
|
async def lc(
|
||||||
raw_user_message) -> None:
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
||||||
try:
|
) -> None:
|
||||||
# sending typing state
|
# sending typing state
|
||||||
await self.client.room_typing(room_id)
|
await self.client.room_typing(room_id)
|
||||||
if self.flowise_api_key is not None:
|
if self.flowise_api_key is not None:
|
||||||
headers = {'Authorization': f'Bearer {self.flowise_api_key}'}
|
headers = {"Authorization": f"Bearer {self.flowise_api_key}"}
|
||||||
response = await asyncio.to_thread(flowise_query,
|
response = await asyncio.to_thread(
|
||||||
self.flowise_api_url, prompt, headers)
|
flowise_query, self.flowise_api_url, prompt, headers
|
||||||
else:
|
)
|
||||||
response = await asyncio.to_thread(flowise_query,
|
else:
|
||||||
self.flowise_api_url, prompt)
|
response = await asyncio.to_thread(
|
||||||
await send_room_message(self.client, room_id, reply_message=response,
|
flowise_query, self.flowise_api_url, prompt
|
||||||
reply_to_event_id="", sender_id=sender_id,
|
)
|
||||||
user_message=raw_user_message,
|
await send_room_message(
|
||||||
markdown_formatted=self.markdown_formatted)
|
self.client,
|
||||||
except Exception as e:
|
room_id,
|
||||||
raise Exception(e)
|
reply_message=response,
|
||||||
|
reply_to_event_id="",
|
||||||
|
sender_id=sender_id,
|
||||||
|
user_message=raw_user_message,
|
||||||
|
markdown_formatted=self.markdown_formatted,
|
||||||
|
)
|
||||||
|
|
||||||
|
# !talk command
|
||||||
|
async def talk(
|
||||||
|
self, room_id, reply_to_event_id, prompt, sender_id, raw_user_message
|
||||||
|
) -> None:
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
|
# !goon command
|
||||||
|
async def goon(
|
||||||
|
self, room_id, reply_to_event_id, sender_id, raw_user_message
|
||||||
|
) -> None:
|
||||||
|
# sending typing state
|
||||||
|
await self.client.room_typing(room_id)
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
|
# !new command
|
||||||
|
async def new(
|
||||||
|
self, room_id, reply_to_event_id, sender_id, raw_user_message
|
||||||
|
) -> None:
|
||||||
|
self.pandora_init(sender_id)
|
||||||
|
content = "New conversation created, please use !talk to start chatting!"
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
# !pic command
|
# !pic command
|
||||||
|
|
||||||
async def pic(self, room_id, prompt):
|
async def pic(self, room_id, prompt):
|
||||||
try:
|
await self.client.room_typing(room_id, timeout=180000)
|
||||||
await self.client.room_typing(room_id, timeout=180000)
|
# generate image
|
||||||
# generate image
|
links = await self.imageGen.get_images(prompt)
|
||||||
try:
|
image_path_list = await self.imageGen.save_images(
|
||||||
|
links, "images", self.output_four_images
|
||||||
links = await self.imageGen.get_images(prompt)
|
)
|
||||||
image_path_list = await self.imageGen.save_images(links, "images",
|
# send image
|
||||||
self.output_four_images)
|
for image_path in image_path_list:
|
||||||
except Exception as e:
|
await send_room_image(self.client, room_id, image_path)
|
||||||
logger.error(f"Image Generation error: {e}", exc_info=True)
|
await self.client.room_typing(room_id, typing_state=False)
|
||||||
raise Exception(e)
|
|
||||||
|
|
||||||
# send image
|
|
||||||
try:
|
|
||||||
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:
|
|
||||||
logger.error(e, exc_info=True)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(e, exc_info=True)
|
|
||||||
|
|
||||||
# !help command
|
# !help command
|
||||||
async def help(self, room_id):
|
async def help(self, room_id):
|
||||||
try:
|
help_info = (
|
||||||
# sending typing state
|
"!gpt [prompt], generate response without context conversation\n"
|
||||||
await self.client.room_typing(room_id)
|
+ "!chat [prompt], chat with context conversation\n"
|
||||||
help_info = "!gpt [prompt], generate response without context conversation\n" + \
|
+ "!bing [prompt], chat with context conversation powered by Bing AI\n"
|
||||||
"!chat [prompt], chat with context conversation\n" + \
|
+ "!bard [prompt], chat with Google's Bard\n"
|
||||||
"!bing [prompt], chat with context conversation powered by Bing AI\n" + \
|
+ "!pic [prompt], Image generation by Microsoft Bing\n"
|
||||||
"!bard [prompt], chat with Google's Bard\n" + \
|
+ "!talk [content], talk using chatgpt web (pandora)\n"
|
||||||
"!pic [prompt], Image generation by Microsoft Bing\n" + \
|
+ "!goon, continue the incomplete conversation (pandora)\n"
|
||||||
"!lc [prompt], chat using langchain api\n" + \
|
+ "!new, start a new conversation (pandora)\n"
|
||||||
"!help, help message" # noqa: E501
|
+ "!lc [prompt], chat using langchain api\n"
|
||||||
|
+ "!help, help message"
|
||||||
|
) # noqa: E501
|
||||||
|
|
||||||
await send_room_message(self.client, room_id, reply_message=help_info)
|
await send_room_message(self.client, room_id, reply_message=help_info)
|
||||||
except Exception as e:
|
|
||||||
logger.error(e, exc_info=True)
|
|
||||||
|
|
||||||
# bot login
|
# bot login
|
||||||
async def login(self) -> None:
|
async def login(self) -> None:
|
||||||
|
@ -702,16 +894,15 @@ class Bot:
|
||||||
# import keys
|
# import keys
|
||||||
async def import_keys(self):
|
async def import_keys(self):
|
||||||
resp = await self.client.import_keys(
|
resp = await self.client.import_keys(
|
||||||
self.import_keys_path,
|
self.import_keys_path, self.import_keys_password
|
||||||
self.import_keys_password
|
|
||||||
)
|
)
|
||||||
if isinstance(resp, EncryptionError):
|
if isinstance(resp, EncryptionError):
|
||||||
logger.error(f"import_keys failed with {resp}")
|
logger.error(f"import_keys failed with {resp}")
|
||||||
else:
|
else:
|
||||||
logger.info(
|
logger.info(
|
||||||
"import_keys success, please remove import_keys configuration!!!")
|
"import_keys success, please remove import_keys configuration!!!"
|
||||||
|
)
|
||||||
|
|
||||||
# sync messages in the room
|
# sync messages in the room
|
||||||
async def sync_forever(self, timeout=30000, full_state=True) -> None:
|
async def sync_forever(self, timeout=30000, full_state=True) -> None:
|
||||||
|
|
||||||
await self.client.sync_forever(timeout=timeout, full_state=full_state)
|
await self.client.sync_forever(timeout=timeout, full_state=full_state)
|
||||||
|
|
16
compose.yaml
16
compose.yaml
|
@ -2,7 +2,7 @@ services:
|
||||||
app:
|
app:
|
||||||
image: hibobmaster/matrixchatgptbot:latest
|
image: hibobmaster/matrixchatgptbot:latest
|
||||||
container_name: matrix_chatgpt_bot
|
container_name: matrix_chatgpt_bot
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
# build:
|
# build:
|
||||||
# context: .
|
# context: .
|
||||||
# dockerfile: ./Dockerfile
|
# dockerfile: ./Dockerfile
|
||||||
|
@ -19,13 +19,23 @@ services:
|
||||||
- matrix_network
|
- matrix_network
|
||||||
# api:
|
# api:
|
||||||
# # bing api
|
# # bing api
|
||||||
# image: ghcr.io/waylaidwanderer/node-chatgpt-api:latest
|
# image: hibobmaster/node-chatgpt-api:latest
|
||||||
# container_name: node-chatgpt-api
|
# container_name: node-chatgpt-api
|
||||||
# restart: always
|
# restart: unless-stopped
|
||||||
# volumes:
|
# volumes:
|
||||||
# - ./settings.js:/var/chatgpt-api/settings.js
|
# - ./settings.js:/var/chatgpt-api/settings.js
|
||||||
# networks:
|
# networks:
|
||||||
# - matrix_network
|
# - matrix_network
|
||||||
|
|
||||||
|
# pandora:
|
||||||
|
# image: pengzhile/pandora
|
||||||
|
# container_name: pandora
|
||||||
|
# restart: unless-stopped
|
||||||
|
# environment:
|
||||||
|
# - PANDORA_ACCESS_TOKEN="xxxxxxxxxxxxxx"
|
||||||
|
# - PANDORA_SERVER="0.0.0.0:8008"
|
||||||
|
# networks:
|
||||||
|
# - matrix_network
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
matrix_network:
|
matrix_network:
|
||||||
|
|
|
@ -15,5 +15,7 @@
|
||||||
"import_keys_path": "element-keys.txt",
|
"import_keys_path": "element-keys.txt",
|
||||||
"import_keys_password": "xxxxxxxxx",
|
"import_keys_password": "xxxxxxxxx",
|
||||||
"flowise_api_url": "http://localhost:3000/api/v1/prediction/6deb3c89-45bf-4ac4-a0b0-b2d5ef249d21",
|
"flowise_api_url": "http://localhost:3000/api/v1/prediction/6deb3c89-45bf-4ac4-a0b0-b2d5ef249d21",
|
||||||
"flowise_api_key": "U3pe0bbVDWOyoJtsDzFJjRvHKTP3FRjODwuM78exC3A="
|
"flowise_api_key": "U3pe0bbVDWOyoJtsDzFJjRvHKTP3FRjODwuM78exC3A=",
|
||||||
|
"pandora_api_endpoint": "http://127.0.0.1:8008",
|
||||||
|
"pandora_api_model": "text-davinci-002-render-sha-mobile"
|
||||||
}
|
}
|
||||||
|
|
105
main.py
105
main.py
|
@ -9,61 +9,66 @@ logger = getlogger()
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
need_import_keys = False
|
need_import_keys = False
|
||||||
if os.path.exists('config.json'):
|
if os.path.exists("config.json"):
|
||||||
fp = open('config.json', 'r', encoding="utf8")
|
fp = open("config.json", "r", encoding="utf8")
|
||||||
config = json.load(fp)
|
config = json.load(fp)
|
||||||
|
|
||||||
matrix_bot = Bot(homeserver=config.get('homeserver'),
|
matrix_bot = Bot(
|
||||||
user_id=config.get('user_id'),
|
homeserver=config.get("homeserver"),
|
||||||
password=config.get('password'),
|
user_id=config.get("user_id"),
|
||||||
device_id=config.get('device_id'),
|
password=config.get("password"),
|
||||||
room_id=config.get('room_id'),
|
device_id=config.get("device_id"),
|
||||||
api_key=config.get('api_key'),
|
room_id=config.get("room_id"),
|
||||||
bing_api_endpoint=config.get('bing_api_endpoint'),
|
api_key=config.get("api_key"),
|
||||||
access_token=config.get('access_token'),
|
bing_api_endpoint=config.get("bing_api_endpoint"),
|
||||||
bard_token=config.get('bard_token'),
|
access_token=config.get("access_token"),
|
||||||
jailbreakEnabled=config.get('jailbreakEnabled'),
|
bard_token=config.get("bard_token"),
|
||||||
bing_auth_cookie=config.get('bing_auth_cookie'),
|
jailbreakEnabled=config.get("jailbreakEnabled"),
|
||||||
markdown_formatted=config.get('markdown_formatted'),
|
bing_auth_cookie=config.get("bing_auth_cookie"),
|
||||||
output_four_images=config.get('output_four_images'),
|
markdown_formatted=config.get("markdown_formatted"),
|
||||||
import_keys_path=config.get('import_keys_path'),
|
output_four_images=config.get("output_four_images"),
|
||||||
import_keys_password=config.get(
|
import_keys_path=config.get("import_keys_path"),
|
||||||
'import_keys_password'),
|
import_keys_password=config.get("import_keys_password"),
|
||||||
flowise_api_url=config.get('flowise_api_url'),
|
flowise_api_url=config.get("flowise_api_url"),
|
||||||
flowise_api_key=config.get('flowise_api_key'),
|
flowise_api_key=config.get("flowise_api_key"),
|
||||||
)
|
pandora_api_endpoint=config.get("pandora_api_endpoint"),
|
||||||
if config.get('import_keys_path') and \
|
pandora_api_model=config.get("pandora_api_model"),
|
||||||
config.get('import_keys_password') is not None:
|
)
|
||||||
|
if (
|
||||||
|
config.get("import_keys_path")
|
||||||
|
and config.get("import_keys_password") is not None
|
||||||
|
):
|
||||||
need_import_keys = True
|
need_import_keys = True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
matrix_bot = Bot(homeserver=os.environ.get('HOMESERVER'),
|
matrix_bot = Bot(
|
||||||
user_id=os.environ.get('USER_ID'),
|
homeserver=os.environ.get("HOMESERVER"),
|
||||||
password=os.environ.get('PASSWORD'),
|
user_id=os.environ.get("USER_ID"),
|
||||||
device_id=os.environ.get("DEVICE_ID"),
|
password=os.environ.get("PASSWORD"),
|
||||||
room_id=os.environ.get("ROOM_ID"),
|
device_id=os.environ.get("DEVICE_ID"),
|
||||||
api_key=os.environ.get("OPENAI_API_KEY"),
|
room_id=os.environ.get("ROOM_ID"),
|
||||||
bing_api_endpoint=os.environ.get("BING_API_ENDPOINT"),
|
api_key=os.environ.get("OPENAI_API_KEY"),
|
||||||
access_token=os.environ.get("ACCESS_TOKEN"),
|
bing_api_endpoint=os.environ.get("BING_API_ENDPOINT"),
|
||||||
bard_token=os.environ.get("BARD_TOKEN"),
|
access_token=os.environ.get("ACCESS_TOKEN"),
|
||||||
jailbreakEnabled=os.environ.get(
|
bard_token=os.environ.get("BARD_TOKEN"),
|
||||||
"JAILBREAKENABLED", "false").lower() \
|
jailbreakEnabled=os.environ.get("JAILBREAKENABLED", "false").lower()
|
||||||
in ('true', '1', 't'),
|
in ("true", "1", "t"),
|
||||||
bing_auth_cookie=os.environ.get("BING_AUTH_COOKIE"),
|
bing_auth_cookie=os.environ.get("BING_AUTH_COOKIE"),
|
||||||
markdown_formatted=os.environ.get(
|
markdown_formatted=os.environ.get("MARKDOWN_FORMATTED", "false").lower()
|
||||||
"MARKDOWN_FORMATTED", "false").lower() \
|
in ("true", "1", "t"),
|
||||||
in ('true', '1', 't'),
|
output_four_images=os.environ.get("OUTPUT_FOUR_IMAGES", "false").lower()
|
||||||
output_four_images=os.environ.get(
|
in ("true", "1", "t"),
|
||||||
"OUTPUT_FOUR_IMAGES", "false").lower() \
|
import_keys_path=os.environ.get("IMPORT_KEYS_PATH"),
|
||||||
in ('true', '1', 't'),
|
import_keys_password=os.environ.get("IMPORT_KEYS_PASSWORD"),
|
||||||
import_keys_path=os.environ.get("IMPORT_KEYS_PATH"),
|
flowise_api_url=os.environ.get("FLOWISE_API_URL"),
|
||||||
import_keys_password=os.environ.get(
|
flowise_api_key=os.environ.get("FLOWISE_API_KEY"),
|
||||||
"IMPORT_KEYS_PASSWORD"),
|
pandora_api_endpoint=os.environ.get("PANDORA_API_ENDPOINT"),
|
||||||
flowise_api_url=os.environ.get("FLOWISE_API_URL"),
|
pandora_api_model=os.environ.get("PANDORA_API_MODEL"),
|
||||||
flowise_api_key=os.environ.get("FLOWISE_API_KEY"),
|
)
|
||||||
)
|
if (
|
||||||
if os.environ.get("IMPORT_KEYS_PATH") \
|
os.environ.get("IMPORT_KEYS_PATH")
|
||||||
and os.environ.get("IMPORT_KEYS_PASSWORD") is not None:
|
and os.environ.get("IMPORT_KEYS_PASSWORD") is not None
|
||||||
|
):
|
||||||
need_import_keys = True
|
need_import_keys = True
|
||||||
|
|
||||||
await matrix_bot.login()
|
await matrix_bot.login()
|
||||||
|
|
106
pandora.py
Normal file
106
pandora.py
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
# https://github.com/pengzhile/pandora/blob/master/doc/HTTP-API.md
|
||||||
|
import uuid
|
||||||
|
import aiohttp
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
|
||||||
|
class Pandora:
|
||||||
|
def __init__(self, api_endpoint: str, clientSession: aiohttp.ClientSession) -> None:
|
||||||
|
self.api_endpoint = api_endpoint.rstrip("/")
|
||||||
|
self.session = clientSession
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
await self.session.close()
|
||||||
|
|
||||||
|
async def gen_title(self, data: dict, conversation_id: str) -> None:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
"model": "",
|
||||||
|
"message_id": "",
|
||||||
|
}
|
||||||
|
:param data: dict
|
||||||
|
:param conversation_id: str
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
api_endpoint = (
|
||||||
|
self.api_endpoint + f"/api/conversation/gen_title/{conversation_id}"
|
||||||
|
)
|
||||||
|
async with self.session.post(api_endpoint, json=data) as resp:
|
||||||
|
return await resp.json()
|
||||||
|
|
||||||
|
async def talk(self, data: dict) -> None:
|
||||||
|
api_endpoint = self.api_endpoint + "/api/conversation/talk"
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
"prompt": "",
|
||||||
|
"model": "",
|
||||||
|
"parent_message_id": "",
|
||||||
|
"conversation_id": "", # ignore at the first time
|
||||||
|
"stream": True,
|
||||||
|
}
|
||||||
|
:param data: dict
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
data["message_id"] = str(uuid.uuid4())
|
||||||
|
async with self.session.post(api_endpoint, json=data) as resp:
|
||||||
|
return await resp.json()
|
||||||
|
|
||||||
|
async def goon(self, data: dict) -> None:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
"model": "",
|
||||||
|
"parent_message_id": "",
|
||||||
|
"conversation_id": "",
|
||||||
|
"stream": True,
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
api_endpoint = self.api_endpoint + "/api/conversation/goon"
|
||||||
|
async with self.session.post(api_endpoint, json=data) as resp:
|
||||||
|
return await resp.json()
|
||||||
|
|
||||||
|
|
||||||
|
async def test():
|
||||||
|
model = "text-davinci-002-render-sha-mobile"
|
||||||
|
api_endpoint = "http://127.0.0.1:8008"
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
client = Pandora(api_endpoint, session)
|
||||||
|
conversation_id = None
|
||||||
|
parent_message_id = str(uuid.uuid4())
|
||||||
|
first_time = True
|
||||||
|
async with client:
|
||||||
|
while True:
|
||||||
|
prompt = input("BobMaster: ")
|
||||||
|
if conversation_id:
|
||||||
|
data = {
|
||||||
|
"prompt": prompt,
|
||||||
|
"model": model,
|
||||||
|
"parent_message_id": parent_message_id,
|
||||||
|
"conversation_id": conversation_id,
|
||||||
|
"stream": False,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
data = {
|
||||||
|
"prompt": prompt,
|
||||||
|
"model": model,
|
||||||
|
"parent_message_id": parent_message_id,
|
||||||
|
"stream": False,
|
||||||
|
}
|
||||||
|
response = await client.talk(data)
|
||||||
|
conversation_id = response["conversation_id"]
|
||||||
|
parent_message_id = response["message"]["id"]
|
||||||
|
content = response["message"]["content"]["parts"][0]
|
||||||
|
print("ChatGPT: " + content + "\n")
|
||||||
|
if first_time:
|
||||||
|
first_time = False
|
||||||
|
data = {
|
||||||
|
"model": model,
|
||||||
|
"message_id": parent_message_id,
|
||||||
|
}
|
||||||
|
response = await client.gen_title(data, conversation_id)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(test())
|
Loading…
Reference in a new issue