Initial commit
This commit is contained in:
parent
4f71a33630
commit
417f01a778
137
__init__.py
Normal file
137
__init__.py
Normal file
@ -0,0 +1,137 @@
|
||||
"""
|
||||
This module contains functions for interfacing with node-chatgpt-api
|
||||
|
||||
Functions:
|
||||
- TODO
|
||||
- do not put url into database... why the fuck
|
||||
"""
|
||||
import aiohttp
|
||||
from markdown import markdown
|
||||
from opsdroid.skill import Skill
|
||||
from opsdroid.matchers import match_catchall
|
||||
|
||||
|
||||
################################################################################
|
||||
# Helper functions #
|
||||
################################################################################
|
||||
async def get_api_response(question_text, conversation_context, api_params):
|
||||
"""
|
||||
This communicates with the API
|
||||
"""
|
||||
params = api_params['params']
|
||||
data = {**params, **conversation_context}
|
||||
api_to_use = conversation_context["api_to_use"]
|
||||
if "conversation_keys" in api_params:
|
||||
conversation_keys = api_params['conversation_keys']
|
||||
else:
|
||||
conversation_keys = {}
|
||||
if "prompt" in api_params:
|
||||
prompt = {api_params['prompt']: question_text}
|
||||
data = {**prompt, **params, **conversation_context}
|
||||
else:
|
||||
prompt = question_text
|
||||
data = [question_text, {**data}]
|
||||
|
||||
headers = {'Content-type': 'application/json'}
|
||||
|
||||
async with aiohttp.ClientSession(headers=headers) as session:
|
||||
async with session.post(api_params['api-url'], json=data) as response:
|
||||
response_data = await response.json()
|
||||
conversation_context = {
|
||||
key: response_data[key]
|
||||
for key in conversation_keys if key in response_data
|
||||
}
|
||||
# Some special keys so they don't get lost along the way
|
||||
conversation_context["parentMessageId"] = response_data[
|
||||
"messageId"] if "messageId" in response_data else None
|
||||
conversation_context["api_to_use"] = api_to_use
|
||||
return response_data, conversation_context
|
||||
|
||||
|
||||
################################################################################
|
||||
# Skills #
|
||||
################################################################################
|
||||
@match_catchall(messages_only=True)
|
||||
async def api_conversation(opsdroid, config, message):
|
||||
"""
|
||||
Important variables:
|
||||
thread_id - identifier of the conversation and matrix's thread ID
|
||||
The key for saving and recalling the conversation into/from memory
|
||||
conversation_context - what gets saved in opsdroid memory. This
|
||||
contains the name of the API (based on which configuration will be
|
||||
applied) as well as any variables needed to identify the
|
||||
conversation. It does not contain settings themselves.
|
||||
question_text - the message as sent to opsdroid, always without the hot-word part
|
||||
|
||||
Objects:
|
||||
connector_matrix - the object obtained from opsdroid.get_connector
|
||||
"""
|
||||
# Get connector_matrix object and start the typing notification
|
||||
if message.connector.name == "matrix":
|
||||
connector_matrix = opsdroid.get_connector("matrix")
|
||||
await connector_matrix.connection.room_typing(message.target,
|
||||
typing_state=True)
|
||||
api_to_use = None
|
||||
|
||||
if message.connector.name == "matrix" and 'm.relates_to' in message.raw_event['content']:
|
||||
# Load conversation_context for current thread_id if it exists
|
||||
question_text = message.text
|
||||
thread_id = message.raw_event['content']['m.relates_to']['event_id']
|
||||
conversation_context = await opsdroid.memory.get(thread_id)
|
||||
api_to_use = conversation_context["api_to_use"]
|
||||
else:
|
||||
# This is a new message, the first word is the hot-word
|
||||
hot_word = message.text.split()[0]
|
||||
# Then comes the question
|
||||
question_text = ' '.join(message.text.split()[1:])
|
||||
# Set thread_id for starting a new thread
|
||||
thread_id = message.event_id
|
||||
|
||||
for key in config.get("apis"):
|
||||
if hot_word == config.get("apis")[key]["hot-word"]:
|
||||
api_to_use = key
|
||||
break
|
||||
if api_to_use is None:
|
||||
# Nothing matched. End typing notice and quit the script
|
||||
if message.connector.name == "matrix":
|
||||
await connector_matrix.connection.room_typing(
|
||||
message.target, typing_state=False)
|
||||
return
|
||||
# Generate empty conversation_context
|
||||
conversation_context = {"api_to_use": api_to_use}
|
||||
|
||||
api_params = config.get("apis")[api_to_use]
|
||||
|
||||
# Get response from API
|
||||
try:
|
||||
# pylint: disable=W0612
|
||||
response_data, conversation_context = await get_api_response(
|
||||
question_text, conversation_context, api_params)
|
||||
response_key = api_params["response"]
|
||||
response_value = eval(f"response_data{response_key}") # pylint: disable=W0123
|
||||
except KeyError:
|
||||
response_value = "No such response key was found. Check configuration"
|
||||
|
||||
if message.connector.name == "matrix":
|
||||
# Construct and send a response and save conversation context for matrix
|
||||
message_dict = {
|
||||
"msgtype": "m.text",
|
||||
"body": response_value,
|
||||
"formatted_body": markdown(response_value,
|
||||
extensions=['fenced_code']),
|
||||
"format": "org.matrix.custom.html",
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.thread",
|
||||
"event_id": thread_id,
|
||||
}
|
||||
}
|
||||
|
||||
await connector_matrix.connection.room_send(message.target,
|
||||
"m.room.message",
|
||||
message_dict)
|
||||
await connector_matrix.connection.room_typing(message.target,
|
||||
typing_state=False)
|
||||
await opsdroid.memory.put(thread_id, conversation_context)
|
||||
else:
|
||||
# For non-matrix connectors send a response
|
||||
await message.respond(response_value)
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
markdown
|
Loading…
Reference in New Issue
Block a user