mirror of
https://gitlab.com/octospacc/WinDog.git
synced 2025-06-05 22:09:20 +02:00
Legacy removals, code restructuring, add send_... functions and better help
This commit is contained in:
@@ -1,37 +1,46 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
def cSource(context:EventContext, data:InputMessageData) -> None:
|
||||
SendMessage(context, {"text_plain": ("""\
|
||||
def cSource(context:EventContext, data:InputMessageData):
|
||||
send_message(context, {"text_plain": ("""\
|
||||
* Original Code: {https://gitlab.com/octospacc/WinDog}
|
||||
* Mirror: {https://github.com/octospacc/WinDog}
|
||||
""" + (f"* Modified Code: {{{ModifiedSourceUrl}}}" if ModifiedSourceUrl else ""))})
|
||||
|
||||
def cGdpr(context:EventContext, data:InputMessageData) -> None:
|
||||
def cGdpr(context:EventContext, data:InputMessageData):
|
||||
pass
|
||||
|
||||
def cConfig(context:EventContext, data:InputMessageData) -> None:
|
||||
if not (settings := GetUserSettings(data.user.id)):
|
||||
UserSettingsLimits = {
|
||||
"language": 13,
|
||||
}
|
||||
|
||||
def cConfig(context:EventContext, data:InputMessageData):
|
||||
language = data.user.settings.language
|
||||
if not (settings := UserSettingsData(data.user.id)):
|
||||
User.update(settings=EntitySettings.create()).where(User.id == data.user.id).execute()
|
||||
if (to_set := ObjGet(data, "command.arguments.set")):
|
||||
pass # TODO set in db, but first we need to ensure data is handled safely
|
||||
if (to_get := ObjGet(data, "command.arguments.get")):
|
||||
# TODO show a hint on possible options?
|
||||
return SendMessage(context, OutputMessageData(text_plain=str(ObjGet(data.user.settings, to_get))))
|
||||
settings = UserSettingsData(data.user.id)
|
||||
if not (key := data.command.arguments.get) or (key not in UserSettingsLimits):
|
||||
return send_status_400(context, language)
|
||||
if (value := data.command.body):
|
||||
if len(value) > UserSettingsLimits[key]:
|
||||
return send_status(context, 500, language)
|
||||
EntitySettings.update(**{key: value}).where(EntitySettings.entity == data.user.id).execute()
|
||||
settings = UserSettingsData(data.user.id)
|
||||
if (key):
|
||||
# TODO show a hint on possible options? and add proper text hints for results
|
||||
return send_message(context, {"text_plain": str(obj_get(settings, key))})
|
||||
# TODO show general help when no useful parameters are passed
|
||||
#Cmd = TelegramHandleCmd(update)
|
||||
#if not Cmd: return
|
||||
# ... area: eu, us, ...
|
||||
# ... language: en, it, ...
|
||||
# ... userdata: import, export, delete
|
||||
|
||||
def cPing(context:EventContext, data:InputMessageData) -> None:
|
||||
def cPing(context:EventContext, data:InputMessageData):
|
||||
# nice experiment, but it won't work with Telegram since time is not to milliseconds (?)
|
||||
#time_diff = (time_now := int(time.time())) - (time_sent := data.datetime)
|
||||
#SendMessage(context, OutputMessageData(text_html=f"<b>Pong!</b>\n\n{time_sent} → {time_now} = {time_diff}"))
|
||||
SendMessage(context, OutputMessageData(text_html="<b>Pong!</b>"))
|
||||
#send_message(context, OutputMessageData(text_html=f"<b>Pong!</b>\n\n{time_sent} → {time_now} = {time_diff}"))
|
||||
send_message(context, OutputMessageData(text_html="<b>Pong!</b>"))
|
||||
|
||||
#def cTime(update:Update, context:CallbackContext) -> None:
|
||||
# update.message.reply_markdown_v2(
|
||||
@@ -39,7 +48,7 @@ def cPing(context:EventContext, data:InputMessageData) -> None:
|
||||
# reply_to_message_id=update.message.message_id)
|
||||
|
||||
#def cEval(context:EventContext, data:InputMessageData) -> None:
|
||||
# SendMessage(context, {"Text": choice(Locale.__('eval'))})
|
||||
# send_message(context, {"Text": choice(Locale.__('eval'))})
|
||||
|
||||
RegisterModule(name="Base", endpoints=[
|
||||
SafeNamespace(names=["source"], handler=cSource),
|
||||
|
@@ -1,17 +1,19 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
def cBroadcast(context:EventContext, data:InputMessageData) -> None:
|
||||
def cBroadcast(context:EventContext, data:InputMessageData):
|
||||
language = data.user.settings.language
|
||||
if (data.user.id not in AdminIds) and (data.user.tag not in AdminIds):
|
||||
return SendMessage(context, {"Text": "Permission denied."})
|
||||
destination = data.command.arguments["destination"]
|
||||
text = data.command.body
|
||||
return send_status(context, 403, language)
|
||||
destination = data.command.arguments.destination
|
||||
text = (data.command.body or (data.quoted and data.quoted.text_plain))
|
||||
if not (destination and text):
|
||||
return SendMessage(context, OutputMessageData(text_plain="Bad usage."))
|
||||
SendMessage(context, {"text_plain": text, "room": SafeNamespace(id=destination)})
|
||||
SendMessage(context, {"text_plain": "Executed."})
|
||||
return send_status_400(context, language)
|
||||
result = send_message(context, {"text_plain": text, "room": SafeNamespace(id=destination)})
|
||||
send_message(context, {"text_plain": "Executed."})
|
||||
return result
|
||||
|
||||
RegisterModule(name="Broadcast", endpoints=[
|
||||
SafeNamespace(names=["broadcast"], handler=cBroadcast, body=True, arguments={
|
||||
|
@@ -1,28 +1,49 @@
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
import base64
|
||||
from binascii import Error as binascii_Error
|
||||
import base256
|
||||
|
||||
CodingsAlgorithms = []
|
||||
CodingsMethods = {
|
||||
"encode:ascii85": base64.a85encode,
|
||||
"decode:ascii85": base64.a85decode,
|
||||
"encode:base16": base64.b16encode,
|
||||
"decode:base16": base64.b16decode,
|
||||
"encode:base32": base64.b32encode,
|
||||
"decode:base32": base64.b32decode,
|
||||
"encode:base32hex": base64.b32hexencode,
|
||||
"decode:base32hex": base64.b32hexdecode,
|
||||
"encode:base64": base64.b64encode,
|
||||
"decode:base64": base64.b64decode,
|
||||
"encode:base85": base64.b85encode,
|
||||
"decode:base85": base64.b85decode,
|
||||
"encode:base256": (lambda decoded: base256.encode_string(decoded.decode()).encode()),
|
||||
"decode:base256": (lambda encoded: base256.decode_string(encoded.decode()).encode()),
|
||||
}
|
||||
for method in dict(CodingsMethods):
|
||||
method2 = method.replace("ascii", 'a').replace("base", 'b')
|
||||
CodingsMethods[method2] = CodingsMethods[method]
|
||||
if (name := method.split(':')[1]) not in CodingsAlgorithms:
|
||||
CodingsAlgorithms.append(name)
|
||||
|
||||
def mCodings(context:EventContext, data:InputMessageData):
|
||||
algorithms = ["base64"]
|
||||
methods = {
|
||||
"encode_base64": base64.b64encode,
|
||||
"decode_base64": base64.b64decode,
|
||||
"encode_b64": base64.b64encode,
|
||||
"decode_b64": base64.b64decode,
|
||||
}
|
||||
if (method := ObjGet(methods, f"{data.command.name}_{data.command.arguments.algorithm}")):
|
||||
try:
|
||||
result = method((data.command.body or (data.quoted and data.quoted.text_plain)).encode()).decode()
|
||||
SendMessage(context, {"text_html": f"<pre>{html_escape(result)}</pre>"})
|
||||
except binascii_Error:
|
||||
SendMessage(context, {"text_plain": f"An error occurred."})
|
||||
else:
|
||||
language = data.user.settings.language
|
||||
SendMessage(context, {
|
||||
"text_html": f'{context.endpoint.help_text(language)}\n\n{context.module.get_string("algorithms", language)}: {algorithms}'})
|
||||
language = data.user.settings.language
|
||||
method = obj_get(CodingsMethods, f"{data.command.name}:{data.command.arguments.algorithm}")
|
||||
text = (data.command.body or (data.quoted and data.quoted.text_plain))
|
||||
if not (method and text):
|
||||
return send_status_400(context, language)
|
||||
try:
|
||||
return send_message(context, {
|
||||
"text_html": f"<pre>{html_escape(method(text.encode()).decode())}</pre>"})
|
||||
except Exception:
|
||||
return send_status_error(context, language)
|
||||
|
||||
RegisterModule(name="Codings", group="Geek", endpoints=[
|
||||
SafeNamespace(names=["encode", "decode"], handler=mCodings, body=False, quoted=False, arguments={
|
||||
"algorithm": True,
|
||||
}),
|
||||
}, help_extra=(lambda endpoint, lang: f'{endpoint.module.get_string("algorithms", lang)}: <code>{"</code>, <code>".join(CodingsAlgorithms)}</code>.')),
|
||||
])
|
||||
|
||||
|
0
ModWinDog/Codings/Codings.yaml
Normal file → Executable file
0
ModWinDog/Codings/Codings.yaml
Normal file → Executable file
1
ModWinDog/Codings/requirements.txt
Executable file
1
ModWinDog/Codings/requirements.txt
Executable file
@@ -0,0 +1 @@
|
||||
base256
|
@@ -1,16 +1,16 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
from json import dumps as json_dumps
|
||||
|
||||
# TODO work with links to messages
|
||||
def cDump(context:EventContext, data:InputMessageData):
|
||||
if (message := data.quoted):
|
||||
dump_text = json_dumps(message, default=(lambda obj: obj.__dict__), indent=" ")
|
||||
SendMessage(context, {
|
||||
"text_html": (f'<pre>{html_escape(dump_text)}</pre>' if message
|
||||
else context.endpoint.help_text(data.user.settings.language))})
|
||||
if not (message := data.quoted):
|
||||
return send_status_400(context, data.user.settings.language)
|
||||
text = json_dumps(message, default=(lambda obj: obj.__dict__), indent=" ")
|
||||
return send_message(context, {"text_html": f'<pre>{html_escape(text)}</pre>'})
|
||||
|
||||
RegisterModule(name="Dumper", group="Geek", endpoints=[
|
||||
SafeNamespace(names=["dump"], handler=cDump, quoted=True),
|
||||
|
@@ -3,9 +3,9 @@
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
def cEcho(context:EventContext, data:InputMessageData) -> None:
|
||||
if not (text := ObjGet(data, "command.body")):
|
||||
return SendMessage(context, {
|
||||
def cEcho(context:EventContext, data:InputMessageData):
|
||||
if not (text := data.command.body):
|
||||
return send_message(context, {
|
||||
"text_html": context.endpoint.get_string("empty", data.user.settings.language)})
|
||||
prefix = f'<a href="{data.message_url}">🗣️</a> '
|
||||
if len(data.command.tokens) == 2: # text is a single word
|
||||
@@ -18,7 +18,7 @@ def cEcho(context:EventContext, data:InputMessageData) -> None:
|
||||
# word is not ascii, probably an emoji (altough not necessarily)
|
||||
# so just pass it as is (useful for Telegram emojis)
|
||||
prefix = ''
|
||||
SendMessage(context, {"text_html": (prefix + html_escape(text))})
|
||||
return send_message(context, {"text_html": (prefix + html_escape(text))})
|
||||
|
||||
RegisterModule(name="Echo", endpoints=[
|
||||
SafeNamespace(names=["echo"], handler=cEcho, body=True),
|
||||
|
@@ -1,21 +1,21 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
from g4f.client import Client as G4FClient
|
||||
|
||||
g4fClient = G4FClient()
|
||||
|
||||
def cGpt(context:EventContext, data:InputMessageData) -> None:
|
||||
def cGpt(context:EventContext, data:InputMessageData):
|
||||
if not (prompt := data.command.body):
|
||||
return SendMessage(context, {"text_plain": "You must type some text."})
|
||||
return send_status_400(context, data.user.settings.language)
|
||||
output = None
|
||||
while not output or output.startswith("sorry, 您的ip已由于触发防滥用检测而被封禁,本服务网址是"): # quick fix for a strange ratelimit message
|
||||
output = ""
|
||||
for completion in g4fClient.chat.completions.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": prompt}], stream=True):
|
||||
output += (completion.choices[0].delta.content or "")
|
||||
return SendMessage(context, {"text_plain": f"[🤖️ GPT]\n\n{output}"})
|
||||
return send_message(context, {"text_plain": f"[🤖️ GPT]\n\n{output}"})
|
||||
|
||||
RegisterModule(name="GPT", endpoints=[
|
||||
SafeNamespace(names=["gpt", "chatgpt"], handler=cGpt, body=True),
|
||||
|
@@ -3,4 +3,6 @@ endpoints:
|
||||
summary:
|
||||
en: >
|
||||
Sends a message to GPT to get back a response. Note: conversations are not yet supported, and this is more standard GPT than ChatGPT, and in general there are many bugs!
|
||||
body:
|
||||
en: Prompt
|
||||
|
||||
|
@@ -1,23 +1,21 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
import hashlib
|
||||
|
||||
def cHash(context:EventContext, data:InputMessageData):
|
||||
text_input = (data.command.body or (data.quoted and data.quoted.text_plain))
|
||||
algorithm = data.command.arguments.algorithm
|
||||
language = data.user.settings.language
|
||||
if not (text_input and (algorithm in hashlib.algorithms_available)):
|
||||
return SendMessage(context, {
|
||||
"text_html": f'{context.endpoint.help_text(language)}\n\n{context.endpoint.get_string("algorithms", language)}: {hashlib.algorithms_available}'})
|
||||
hashed = hashlib.new(algorithm, text_input.encode()).hexdigest()
|
||||
return SendMessage(context, {"text_html": f"<pre>{hashed}</pre>"})
|
||||
return send_status_400(context, data.user.settings.language)
|
||||
return send_message(context, {
|
||||
"text_html": f"<pre>{html_escape(hashlib.new(algorithm, text_input.encode()).hexdigest())}</pre>"})
|
||||
|
||||
RegisterModule(name="Hashing", group="Geek", endpoints=[
|
||||
SafeNamespace(names=["hash"], handler=cHash, body=False, quoted=False, arguments={
|
||||
"algorithm": True,
|
||||
}),
|
||||
}, help_extra=(lambda endpoint, lang: f'{endpoint.get_string("algorithms", lang)}: <code>{"</code>, <code>".join(hashlib.algorithms_available)}</code>.')),
|
||||
])
|
||||
|
||||
|
@@ -1,23 +1,32 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
# TODO: implement /help <commandname> feature
|
||||
def cHelp(context:EventContext, data:InputMessageData) -> None:
|
||||
text = (context.endpoint.get_string(lang=data.user.settings.language) or '').strip()
|
||||
language = data.user.settings.language
|
||||
for module in Modules:
|
||||
summary = Modules[module].get_string("summary", language)
|
||||
endpoints = Modules[module].endpoints
|
||||
text += (f"\n\n{module}" + (f": {summary}" if summary else ''))
|
||||
for endpoint in endpoints:
|
||||
summary = Modules[module].get_string(f"endpoints.{endpoint.names[0]}.summary", language)
|
||||
text += (f"\n* /{', /'.join(endpoint.names)}" + (f": {summary}" if summary else ''))
|
||||
text = text.strip()
|
||||
SendMessage(context, {"text_html": text})
|
||||
prefix = data.command.prefix
|
||||
if (endpoint := data.command.arguments.endpoint):
|
||||
if endpoint[0] in CommandPrefixes:
|
||||
endpoint = endpoint[1:]
|
||||
if endpoint in Endpoints:
|
||||
return send_message(context, {"text_html": get_help_text(endpoint, language, prefix)})
|
||||
text = (context.endpoint.get_string(lang=data.user.settings.language) or '').strip()
|
||||
for group in ModuleGroups:
|
||||
text += f"\n\n[ {group} ]"
|
||||
for module in ModuleGroups[group]:
|
||||
summary = Modules[module].get_string("summary", language)
|
||||
endpoints = Modules[module].endpoints
|
||||
text += (f"\n\n{module}" + (f": {summary}" if summary else ''))
|
||||
for endpoint in endpoints:
|
||||
summary = Modules[module].get_string(f"endpoints.{endpoint.names[0]}.summary", language)
|
||||
text += (f"\n* {prefix}{', {prefix}'.join(endpoint.names)}" + (f": {summary}" if summary else ''))
|
||||
text = text.strip()
|
||||
return send_message(context, {"text_html": text})
|
||||
|
||||
RegisterModule(name="Help", group="Basic", endpoints=[
|
||||
SafeNamespace(names=["help"], handler=cHelp),
|
||||
SafeNamespace(names=["help"], handler=cHelp, arguments={
|
||||
"endpoint": False,
|
||||
}),
|
||||
])
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
""" # windog config start # """
|
||||
|
||||
@@ -12,10 +12,14 @@ MicrosoftBingSettings = {}
|
||||
from urlextract import URLExtract
|
||||
from urllib.request import urlopen, Request
|
||||
|
||||
def RandomHexString(length:int) -> str:
|
||||
return ''.join([randchoice('0123456789abcdef') for i in range(length)])
|
||||
|
||||
def HttpReq(url:str, method:str|None=None, *, body:bytes=None, headers:dict[str, str]={"User-Agent": WebUserAgent}):
|
||||
return urlopen(Request(url, method=method, data=body, headers=headers))
|
||||
|
||||
def cEmbedded(context:EventContext, data:InputMessageData) -> None:
|
||||
def cEmbedded(context:EventContext, data:InputMessageData):
|
||||
language = data.user.settings.language
|
||||
if len(data.command.tokens) >= 2:
|
||||
# Find links in command body
|
||||
text = (data.text_markdown + ' ' + data.text_plain)
|
||||
@@ -23,9 +27,7 @@ def cEmbedded(context:EventContext, data:InputMessageData) -> None:
|
||||
# Find links in quoted message
|
||||
text = ((quoted.text_markdown or '') + ' ' + (quoted.text_plain or '') + ' ' + (quoted.text_html or ''))
|
||||
else:
|
||||
# TODO Error message
|
||||
return
|
||||
pass
|
||||
return send_status_400(context, language)
|
||||
urls = URLExtract().find_urls(text)
|
||||
if len(urls) > 0:
|
||||
proto = 'https://'
|
||||
@@ -47,55 +49,57 @@ def cEmbedded(context:EventContext, data:InputMessageData) -> None:
|
||||
elif urlDomain == "vm.tiktok.com":
|
||||
urlDomain = "vm.vxtiktok.com"
|
||||
url = (urlDomain + '/' + '/'.join(url.split('/')[1:]))
|
||||
SendMessage(context, {"text_plain": f"{{{proto}{url}}}"})
|
||||
# else TODO error message?
|
||||
return send_message(context, {"text_plain": f"{{{proto}{url}}}"})
|
||||
return send_message(context, {"text_plain": "No links found."})
|
||||
|
||||
def cWeb(context:EventContext, data:InputMessageData) -> None:
|
||||
def cWeb(context:EventContext, data:InputMessageData):
|
||||
language = data.user.settings.language
|
||||
if not (query := data.command.body):
|
||||
return # TODO show message
|
||||
return send_status_400(context, language)
|
||||
try:
|
||||
QueryUrl = urlparse.quote(query)
|
||||
Req = HttpReq(f'https://html.duckduckgo.com/html?q={QueryUrl}')
|
||||
Caption = f'🦆🔎 "{query}": https://duckduckgo.com/?q={QueryUrl}\n\n'
|
||||
Index = 0
|
||||
for Line in Req.read().decode().replace('\t', ' ').splitlines():
|
||||
if ' class="result__a" ' in Line and ' href="//duckduckgo.com/l/?uddg=' in Line:
|
||||
Index += 1
|
||||
Link = urlparse.unquote(Line.split(' href="//duckduckgo.com/l/?uddg=')[1].split('&rut=')[0])
|
||||
Title = Line.strip().split('</a>')[0].strip().split('</span>')[-1].strip().split('>')
|
||||
if len(Title) > 1:
|
||||
Title = html_unescape(Title[1].strip())
|
||||
Caption += f'[{Index}] {Title} : {{{Link}}}\n\n'
|
||||
query_url = urlparse.quote(query)
|
||||
request = HttpReq(f'https://html.duckduckgo.com/html?q={query_url}')
|
||||
caption = f'🦆🔎 "{query}": https://duckduckgo.com/?q={query_url}\n\n'
|
||||
index = 0
|
||||
for line in request.read().decode().replace('\t', ' ').splitlines():
|
||||
if ' class="result__a" ' in line and ' href="//duckduckgo.com/l/?uddg=' in line:
|
||||
index += 1
|
||||
link = urlparse.unquote(line.split(' href="//duckduckgo.com/l/?uddg=')[1].split('&rut=')[0])
|
||||
title = line.strip().split('</a>')[0].strip().split('</span>')[-1].strip().split('>')
|
||||
if len(title) > 1:
|
||||
title = html_unescape(title[1].strip())
|
||||
caption += f'[{index}] {title} : {{{link}}}\n\n'
|
||||
else:
|
||||
continue
|
||||
SendMessage(context, {"TextPlain": f'{Caption}...'})
|
||||
return send_message(context, {"text_plain": f'{caption}...'})
|
||||
except Exception:
|
||||
raise
|
||||
return send_status_error(context, language)
|
||||
|
||||
def cImages(context:EventContext, data:InputMessageData) -> None:
|
||||
def cImages(context:EventContext, data:InputMessageData):
|
||||
pass
|
||||
|
||||
def cNews(context:EventContext, data:InputMessageData) -> None:
|
||||
def cNews(context:EventContext, data:InputMessageData):
|
||||
pass
|
||||
|
||||
def cTranslate(context:EventContext, data:InputMessageData) -> None:
|
||||
def cTranslate(context:EventContext, data:InputMessageData):
|
||||
language = data.user.settings.language
|
||||
instances = ["lingva.ml", "lingva.lunar.icu"]
|
||||
language_to = data.command.arguments["language_to"]
|
||||
language_to = data.command.arguments.language_to
|
||||
text_input = (data.command.body or (data.quoted and data.quoted.text_plain))
|
||||
if not (text_input and language_to):
|
||||
return SendMessage(context, {"TextPlain": f"Usage: /translate <to language> <text>"})
|
||||
return send_status_400(context, language)
|
||||
try:
|
||||
result = json.loads(HttpReq(f'https://{randchoice(instances)}/api/v1/auto/{language_to}/{urlparse.quote(text_input)}').read())
|
||||
SendMessage(context, {"TextPlain": f"[{result['info']['detectedSource']} (auto) -> {language_to}]\n\n{result['translation']}"})
|
||||
return send_message(context, {"text_plain": f"[{result['info']['detectedSource']} (auto) -> {language_to}]\n\n{result['translation']}"})
|
||||
except Exception:
|
||||
raise
|
||||
return send_status_error(context, language)
|
||||
|
||||
# unsplash source appears to be deprecated! <https://old.reddit.com/r/unsplash/comments/s13x4h/what_happened_to_sourceunsplashcom/l65epl8/>
|
||||
#def cUnsplash(context:EventContext, data:InputMessageData) -> None:
|
||||
# try:
|
||||
# Req = HttpReq(f'https://source.unsplash.com/random/?{urlparse.quote(data.command.body)}')
|
||||
# ImgUrl = Req.geturl().split('?')[0]
|
||||
# SendMessage(context, {
|
||||
# send_message(context, {
|
||||
# "TextPlain": f'{{{ImgUrl}}}',
|
||||
# "TextMarkdown": MarkdownCode(ImgUrl, True),
|
||||
# "Media": Req.read(),
|
||||
@@ -103,20 +107,21 @@ def cTranslate(context:EventContext, data:InputMessageData) -> None:
|
||||
# except Exception:
|
||||
# raise
|
||||
|
||||
def cSafebooru(context:EventContext, data:InputMessageData) -> None:
|
||||
ApiUrl = 'https://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags='
|
||||
def cSafebooru(context:EventContext, data:InputMessageData):
|
||||
language = data.user.settings.language
|
||||
api_url = 'https://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags='
|
||||
try:
|
||||
img_id, img_url = None, None
|
||||
if (query := data.command.body):
|
||||
for i in range(7): # retry a bunch of times if we can't find a really random result
|
||||
ImgUrls = HttpReq(f'{ApiUrl}md5:{RandHexStr(3)}%20{urlparse.quote(query)}').read().decode().split(' file_url="')[1:]
|
||||
if ImgUrls:
|
||||
img_urls = HttpReq(f'{api_url}md5:{RandomHexString(3)}%20{urlparse.quote(query)}').read().decode().split(' file_url="')[1:]
|
||||
if img_urls:
|
||||
break
|
||||
if not ImgUrls: # literal search
|
||||
ImgUrls = HttpReq(f'{ApiUrl}{urlparse.quote(query)}').read().decode().split(' file_url="')[1:]
|
||||
if not ImgUrls:
|
||||
return SendMessage(context, {"Text": "Error: Could not get any result from Safebooru."})
|
||||
ImgXml = choice(ImgUrls)
|
||||
if not img_urls: # literal search
|
||||
img_urls = HttpReq(f'{api_url}{urlparse.quote(query)}').read().decode().split(' file_url="')[1:]
|
||||
if not img_urls:
|
||||
return send_status(context, 404, language, "Could not get any result from Safebooru.", summary=False)
|
||||
ImgXml = choice(img_urls)
|
||||
img_url = ImgXml.split('"')[0]
|
||||
img_id = ImgXml.split(' id="')[1].split('"')[0]
|
||||
else:
|
||||
@@ -127,14 +132,14 @@ def cSafebooru(context:EventContext, data:InputMessageData) -> None:
|
||||
img_id = img_url.split('?')[-1]
|
||||
break
|
||||
if img_url:
|
||||
SendMessage(context, OutputMessageData(
|
||||
return send_message(context, OutputMessageData(
|
||||
text_plain=f"[{img_id}]\n{{{img_url}}}",
|
||||
text_html=f"[<code>{img_id}</code>]\n<pre>{img_url}</pre>",
|
||||
media={"url": img_url}))
|
||||
else:
|
||||
pass
|
||||
except Exception as error:
|
||||
raise
|
||||
return send_status_400(context, language)
|
||||
except Exception:
|
||||
return send_status_error(context, language)
|
||||
|
||||
RegisterModule(name="Internet", endpoints=[
|
||||
SafeNamespace(names=["embedded"], handler=cEmbedded, body=False, quoted=False),
|
||||
|
@@ -1,9 +1,9 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
def mMultifun(context:EventContext, data:InputMessageData) -> None:
|
||||
def mMultifun(context:EventContext, data:InputMessageData):
|
||||
reply_to = None
|
||||
fun_strings = {}
|
||||
for key in ("empty", "bot", "self", "others"):
|
||||
@@ -19,7 +19,7 @@ def mMultifun(context:EventContext, data:InputMessageData) -> None:
|
||||
else:
|
||||
if fun_strings["empty"]:
|
||||
text = choice(fun_strings["empty"])
|
||||
SendMessage(context, {"text_html": text, "ReplyTo": reply_to})
|
||||
return send_message(context, {"text_html": text, "ReplyTo": reply_to})
|
||||
|
||||
RegisterModule(name="Multifun", endpoints=[
|
||||
SafeNamespace(names=["hug", "pat", "poke", "cuddle", "hands", "floor", "sessocto"], handler=mMultifun),
|
||||
|
@@ -3,12 +3,17 @@
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
def mPercenter(context:EventContext, data:InputMessageData) -> None:
|
||||
SendMessage(context, {"text_html": (context.endpoint.get_string(
|
||||
# NOTE: with this implementation there is a 1/100 probability (high!) of result 100.00, which is not always ideal
|
||||
def RandomPercentString() -> str:
|
||||
num = randint(0,100)
|
||||
return (f'{num}.00' if num == 100 else f'{num}.{randint(0,9)}{randint(0,9)}')
|
||||
|
||||
def mPercenter(context:EventContext, data:InputMessageData):
|
||||
return send_message(context, {"text_html": (context.endpoint.get_string(
|
||||
("done" if data.command.body else "empty"),
|
||||
data.user.settings.language
|
||||
) or context.endpoint.help_text(data.user.settings.language)
|
||||
).format(RandPercent(), data.command.body)})
|
||||
) or context.endpoint.get_help_text(data.user.settings.language)
|
||||
).format(RandomPercentString(), data.command.body)})
|
||||
|
||||
RegisterModule(name="Percenter", endpoints=[
|
||||
SafeNamespace(names=["wish", "level"], handler=mPercenter, body=True),
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
""" # windog config start # """
|
||||
|
||||
@@ -22,7 +22,7 @@ def getSelenium() -> tuple[int, Driver]|bool:
|
||||
if index not in currentSeleniumDrivers:
|
||||
currentSeleniumDrivers.append(index)
|
||||
break
|
||||
return (index, Driver(uc=True, headless2=True, user_data_dir=f"./Selenium-WinDog/{index}"))
|
||||
return (index, Driver(uc=True, headless2=True, user_data_dir=f"./Data/Selenium/{index}"))
|
||||
|
||||
def closeSelenium(index:int, driver:Driver) -> None:
|
||||
if driver:
|
||||
@@ -30,19 +30,19 @@ def closeSelenium(index:int, driver:Driver) -> None:
|
||||
driver.close()
|
||||
driver.quit()
|
||||
except:
|
||||
Log(format_exc())
|
||||
app_log(format_exc())
|
||||
if index:
|
||||
currentSeleniumDrivers.remove(index)
|
||||
|
||||
def cDalleSelenium(context:EventContext, data:InputMessageData) -> None:
|
||||
def cDalleSelenium(context:EventContext, data:InputMessageData):
|
||||
warning_text = "has been blocked by Microsoft because it violates their content policy. Further attempts might lead to a ban on your profile. Please review the Code of Conduct for Image Creator in this picture or at https://www.bing.com/new/termsofuseimagecreator#content-policy."
|
||||
if not (prompt := data.command.body):
|
||||
return SendMessage(context, {"Text": "Please tell me what to generate."})
|
||||
return send_message(context, {"text_plain": "Please tell me what to generate."})
|
||||
driver_index, driver = None, None
|
||||
try:
|
||||
driver = getSelenium()
|
||||
if not driver:
|
||||
return SendMessage(context, {"Text": "Couldn't access a web scraping VM as they are all busy. Please try again later."})
|
||||
return send_message(context, {"text_plain": "Couldn't access a web scraping VM as they are all busy. Please try again later."})
|
||||
driver_index, driver = driver
|
||||
driver.get("https://www.bing.com/images/create/")
|
||||
driver.refresh()
|
||||
@@ -50,11 +50,11 @@ def cDalleSelenium(context:EventContext, data:InputMessageData) -> None:
|
||||
driver.find_element('form a[role="button"]').submit()
|
||||
try:
|
||||
driver.find_element('img.gil_err_img[alt="Content warning"]')
|
||||
SendMessage(context, {"Text": f"Content warning: This prompt {warning_text}", "media": {"bytes": open("./Assets/ImageCreator-CodeOfConduct.png", 'rb').read()}})
|
||||
send_message(context, {"text_plain": f"Content warning: This prompt {warning_text}", "media": {"bytes": open("./Assets/ImageCreator-CodeOfConduct.png", 'rb').read()}})
|
||||
return closeSelenium(driver_index, driver)
|
||||
except Exception: # warning element was not found, we should be good
|
||||
pass
|
||||
SendMessage(context, {"Text": "Request sent successfully, please wait..."})
|
||||
send_message(context, {"text_plain": "Request sent successfully, please wait..."})
|
||||
retry_index = 3
|
||||
while retry_index < 12:
|
||||
# note that sometimes generation can still fail and we will never get any image!
|
||||
@@ -64,8 +64,9 @@ def cDalleSelenium(context:EventContext, data:InputMessageData) -> None:
|
||||
if not len(img_list):
|
||||
try:
|
||||
driver.find_element('img.gil_err_img[alt="Unsafe image content detected"]')
|
||||
SendMessage(context, {"Text": f"Unsafe image content detected: This result {warning_text}", "media": {"bytes": open("./Assets/ImageCreator-CodeOfConduct.png", 'rb').read()}})
|
||||
return closeSelenium(driver_index, driver)
|
||||
result = send_message(context, {"text_plain": f"Unsafe image content detected: This result {warning_text}", "media": {"bytes": open("./Assets/ImageCreator-CodeOfConduct.png", 'rb').read()}})
|
||||
closeSelenium(driver_index, driver)
|
||||
return result
|
||||
except: # no error is present, so we just have to wait more for the images
|
||||
continue
|
||||
img_array = []
|
||||
@@ -73,30 +74,32 @@ def cDalleSelenium(context:EventContext, data:InputMessageData) -> None:
|
||||
img_url = img_url.get_attribute("src").split('?')[0]
|
||||
img_array.append({"url": img_url}) #, "bytes": HttpReq(img_url).read()})
|
||||
page_url = driver.current_url.split('?')[0]
|
||||
SendMessage(context, OutputMessageData(
|
||||
result = send_message(context, OutputMessageData(
|
||||
text_plain=f'"{prompt}"\n{{{page_url}}}',
|
||||
text_html=f'"<i>{html_escape(prompt)}</i>"\n<pre>{page_url}</pre>',
|
||||
media=img_array))
|
||||
return closeSelenium(driver_index, driver)
|
||||
closeSelenium(driver_index, driver)
|
||||
return result
|
||||
raise Exception("VM timed out.")
|
||||
except Exception as error:
|
||||
Log(format_exc())
|
||||
SendMessage(context, {"TextPlain": "An unexpected error occurred."})
|
||||
app_log(format_exc())
|
||||
result = send_message(context, {"text_plain": "An unexpected error occurred."})
|
||||
closeSelenium(driver_index, driver)
|
||||
return result
|
||||
|
||||
def cCraiyonSelenium(context:EventContext, data:InputMessageData) -> None:
|
||||
def cCraiyonSelenium(context:EventContext, data:InputMessageData):
|
||||
if not (prompt := data.command.body):
|
||||
return SendMessage(context, {"Text": "Please tell me what to generate."})
|
||||
return send_message(context, {"text_plain": "Please tell me what to generate."})
|
||||
driver_index, driver = None, None
|
||||
try:
|
||||
driver = getSelenium()
|
||||
if not driver:
|
||||
return SendMessage(context, {"Text": "Couldn't access a web scraping VM as they are all busy. Please try again later."})
|
||||
return send_message(context, {"text_plain": "Couldn't access a web scraping VM as they are all busy. Please try again later."})
|
||||
driver_index, driver = driver
|
||||
driver.get("https://www.craiyon.com/")
|
||||
driver.find_element('textarea#prompt').send_keys(prompt)
|
||||
driver.execute_script("arguments[0].click();", driver.find_element('button#generateButton'))
|
||||
SendMessage(context, {"Text": "Request sent successfully, please wait up to 60 seconds..."})
|
||||
send_message(context, {"text_plain": "Request sent successfully, please wait up to 60 seconds..."})
|
||||
retry_index = 3
|
||||
while retry_index < 16:
|
||||
time.sleep(retry_index := retry_index + 1)
|
||||
@@ -106,17 +109,19 @@ def cCraiyonSelenium(context:EventContext, data:InputMessageData) -> None:
|
||||
img_array = []
|
||||
for img_elem in img_list:
|
||||
img_array.append({"url": img_elem.get_attribute("src")}) #, "bytes": HttpReq(img_url).read()})
|
||||
SendMessage(context, {
|
||||
result = send_message(context, {
|
||||
"text_plain": f'"{prompt}"',
|
||||
"text_html": f'"<i>{html_escape(prompt)}</i>"',
|
||||
"media": img_array,
|
||||
})
|
||||
return closeSelenium(driver_index, driver)
|
||||
closeSelenium(driver_index, driver)
|
||||
return result
|
||||
raise Exception("VM timed out.")
|
||||
except Exception as error:
|
||||
Log(format_exc())
|
||||
SendMessage(context, {"TextPlain": "An unexpected error occurred."})
|
||||
app_log(format_exc())
|
||||
result = send_message(context, {"text_plain": "An unexpected error occurred."})
|
||||
closeSelenium(driver_index, driver)
|
||||
return result
|
||||
|
||||
RegisterModule(name="Scrapers", endpoints=[
|
||||
SafeNamespace(names=["dalle"], handler=cDalleSelenium, body=True),
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# ================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ================================== #
|
||||
# ==================================== #
|
||||
# WinDog multi-purpose chatbot #
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
""" # windog config start # """
|
||||
|
||||
@@ -23,11 +23,10 @@ def luaAttributeFilter(obj, attr_name, is_setting):
|
||||
raise AttributeError("Access Denied.")
|
||||
|
||||
# TODO make print behave the same as normal Lua, and expose a function for printing without newlines
|
||||
def cLua(context:EventContext, data:InputMessageData) -> None:
|
||||
def cLua(context:EventContext, data:InputMessageData):
|
||||
# TODO update quoted api getting
|
||||
scriptText = (data.command.body or (data.quoted and data.quoted.text_plain))
|
||||
if not scriptText:
|
||||
return SendMessage(context, {"text_plain": "You must provide some Lua code to execute."})
|
||||
if not (script_text := (data.command.body or (data.quoted and data.quoted.text_plain))):
|
||||
return send_message(context, {"text_plain": "You must provide some Lua code to execute."})
|
||||
luaRuntime = NewLuaRuntime(max_memory=LuaMemoryLimit, register_eval=False, register_builtins=False, attribute_filter=luaAttributeFilter)
|
||||
luaRuntime.eval(f"""(function()
|
||||
_windog = {{ stdout = "" }}
|
||||
@@ -44,13 +43,12 @@ end)()""")
|
||||
elif key not in LuaGlobalsWhitelist:
|
||||
del luaRuntime.globals()[key]
|
||||
try:
|
||||
textOutput = ("[ʟᴜᴀ ꜱᴛᴅᴏᴜᴛ]\n\n" + luaRuntime.eval(f"""(function()
|
||||
_windog.scriptout = (function()\n{scriptText}\nend)()
|
||||
return send_message(context, {"text_plain": ("[ʟᴜᴀ ꜱᴛᴅᴏᴜᴛ]\n\n" + luaRuntime.eval(f"""(function()
|
||||
_windog.scriptout = (function()\n{script_text}\nend)()
|
||||
return _windog.stdout .. (_windog.scriptout or '')
|
||||
end)()"""))
|
||||
except (LuaError, LuaSyntaxError) as error:
|
||||
Log(textOutput := ("Lua Error: " + str(error)))
|
||||
SendMessage(context, {"TextPlain": textOutput})
|
||||
end)()"""))})
|
||||
except (LuaError, LuaSyntaxError):
|
||||
return send_status_error(context, data.user.settings.language)
|
||||
|
||||
RegisterModule(name="Scripting", group="Geek", endpoints=[
|
||||
SafeNamespace(names=["lua"], handler=cLua, body=False, quoted=False),
|
||||
|
@@ -3,8 +3,8 @@
|
||||
# Licensed under AGPLv3 by OctoSpacc #
|
||||
# ==================================== #
|
||||
|
||||
def cStart(context:EventContext, data:InputMessageData) -> None:
|
||||
SendMessage(context, OutputMessageData(
|
||||
def cStart(context:EventContext, data:InputMessageData):
|
||||
return send_message(context, OutputMessageData(
|
||||
text_html=context.endpoint.get_string(
|
||||
"start", data.user.settings.language).format(data.user.name)))
|
||||
|
||||
|
@@ -13,19 +13,28 @@ ExecAllowed = {"date": False, "fortune": False, "neofetch": True, "uptime": Fals
|
||||
import subprocess
|
||||
from re import compile as re_compile
|
||||
|
||||
def cExec(context:EventContext, data:InputMessageData) -> None:
|
||||
if not (len(data.command.tokens) >= 2 and data.command.tokens[1].lower() in ExecAllowed):
|
||||
return SendMessage(context, {"text_plain": "This feature is not implemented [Security Issue]."})
|
||||
def cExec(context:EventContext, data:InputMessageData):
|
||||
language = data.user.settings.language
|
||||
if not (len(data.command.tokens) >= 2):
|
||||
return send_status_400(context, language)
|
||||
if not data.command.tokens[1].lower() in ExecAllowed:
|
||||
return send_status(context, 404, language, context.endpoint.get_string("statuses.404", language), summary=False)
|
||||
command = data.command.tokens[1].lower()
|
||||
output = subprocess.run(
|
||||
("sh", "-c", f"export PATH=$PATH:/usr/games; {command}"),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode()
|
||||
# <https://stackoverflow.com/a/14693789>
|
||||
text = (re_compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])").sub('', output))
|
||||
SendMessage(context, OutputMessageData(
|
||||
text_plain=text, text_html=f"<pre>{html_escape(text)}</pre>"))
|
||||
return send_message(context, {"text_html": f'<pre>{html_escape(text)}</pre>'})
|
||||
|
||||
def cRestart(context:EventContext, data:InputMessageData):
|
||||
if (data.user.id not in AdminIds) and (data.user.tag not in AdminIds):
|
||||
return send_message(context, {"text_plain": "Permission denied."})
|
||||
open("./.WinDog.Restart.lock", 'w').close()
|
||||
return send_message(context, {"text_plain": "Bot restart queued."})
|
||||
|
||||
RegisterModule(name="System", endpoints=[
|
||||
SafeNamespace(names=["exec"], handler=cExec, body=True),
|
||||
SafeNamespace(names=["restart"], handler=cRestart),
|
||||
])
|
||||
|
||||
|
@@ -2,4 +2,7 @@ endpoints:
|
||||
exec:
|
||||
summary:
|
||||
en: Execute a system command from the allowed ones and return stdout+stderr.
|
||||
statuses:
|
||||
404:
|
||||
en: The requested command is not available.
|
||||
|
||||
|
Reference in New Issue
Block a user