mirror of
https://gitlab.com/octospacc/WinDog.git
synced 2025-06-05 22:09:20 +02:00
Update /dalle, /translate; Add /craiyon, /gpt; More restructuring
This commit is contained in:
BIN
Assets/ImageCreator-CodeOfConduct.png
Normal file
BIN
Assets/ImageCreator-CodeOfConduct.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 839 KiB |
@ -13,7 +13,7 @@ LogToFile = True
|
|||||||
DumpToConsole = False
|
DumpToConsole = False
|
||||||
DumpToFile = False
|
DumpToFile = False
|
||||||
|
|
||||||
AdminIds = [ "123456789@telegram", "634314973@telegram", "admin@activitypub@mastodon.example.com", ]
|
AdminIds = [ "telegram:123456789", "telegram:634314973", "activitypub:admin@mastodon.example.com", ]
|
||||||
|
|
||||||
DefaultLang = "en"
|
DefaultLang = "en"
|
||||||
Debug = False
|
Debug = False
|
||||||
|
@ -21,21 +21,24 @@ def MastodonMain() -> bool:
|
|||||||
Mastodon = mastodon.Mastodon(api_base_url=MastodonUrl, access_token=MastodonToken)
|
Mastodon = mastodon.Mastodon(api_base_url=MastodonUrl, access_token=MastodonToken)
|
||||||
class MastodonListener(mastodon.StreamListener):
|
class MastodonListener(mastodon.StreamListener):
|
||||||
def on_notification(self, event):
|
def on_notification(self, event):
|
||||||
if event['type'] == 'mention':
|
MastodonHandler(event)
|
||||||
#OnMessageParsed()
|
|
||||||
message = BeautifulSoup(event['status']['content'], 'html.parser').get_text(' ').strip().replace('\t', ' ')
|
|
||||||
if not message.split('@')[0]:
|
|
||||||
message = ' '.join('@'.join(message.split('@')[1:]).strip().split(' ')[1:]).strip()
|
|
||||||
if message[0] in CmdPrefixes:
|
|
||||||
command = ParseCmd(message)
|
|
||||||
if command:
|
|
||||||
command.messageId = event['status']['id']
|
|
||||||
if command.Name in Endpoints:
|
|
||||||
Endpoints[command.Name]["handler"]({"Event": event, "Manager": Mastodon}, command)
|
|
||||||
Mastodon.stream_user(MastodonListener(), run_async=True)
|
Mastodon.stream_user(MastodonListener(), run_async=True)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def MastodonSender(event, manager, data, destination, textPlain, textMarkdown) -> None:
|
def MastodonHandler(event):
|
||||||
|
if event['type'] == 'mention':
|
||||||
|
#OnMessageParsed()
|
||||||
|
message = BeautifulSoup(event['status']['content'], 'html.parser').get_text(' ').strip().replace('\t', ' ')
|
||||||
|
if not message.split('@')[0]:
|
||||||
|
message = ' '.join('@'.join(message.split('@')[1:]).strip().split(' ')[1:]).strip()
|
||||||
|
if message[0] in CmdPrefixes:
|
||||||
|
command = ParseCmd(message)
|
||||||
|
if command:
|
||||||
|
command.messageId = event['status']['id']
|
||||||
|
if command.Name in Endpoints:
|
||||||
|
Endpoints[command.Name]["handler"]({"Event": event, "Manager": Mastodon}, command)
|
||||||
|
|
||||||
|
def MastodonSender(event, manager, data:OutputMessageData, destination, textPlain, textMarkdown) -> None:
|
||||||
if InDict(data, 'Media'):
|
if InDict(data, 'Media'):
|
||||||
Media = manager.media_post(data['Media'], Magic(mime=True).from_buffer(data['Media']))
|
Media = manager.media_post(data['Media'], Magic(mime=True).from_buffer(data['Media']))
|
||||||
while Media['url'] == 'null':
|
while Media['url'] == 'null':
|
||||||
|
@ -30,23 +30,35 @@ def TelegramMain() -> bool:
|
|||||||
#app.run_polling(allowed_updates=Update.ALL_TYPES)
|
#app.run_polling(allowed_updates=Update.ALL_TYPES)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def TelegramMakeInputMessageData(message:telegram.Message) -> InputMessageData:
|
||||||
|
data = InputMessageData(
|
||||||
|
message_id = f"telegram:{message.message_id}",
|
||||||
|
text_plain = message.text,
|
||||||
|
text_markdown = message.text_markdown_v2,
|
||||||
|
)
|
||||||
|
data.text_auto = GetWeightedText(data.text_markdown, data.text_plain)
|
||||||
|
data.command = ParseCommand(data.text_plain)
|
||||||
|
data.room = SafeNamespace(
|
||||||
|
id = f"telegram:{message.chat.id}",
|
||||||
|
tag = message.chat.username,
|
||||||
|
name = message.chat.title,
|
||||||
|
)
|
||||||
|
data.user = SafeNamespace(
|
||||||
|
id = f"telegram:{message.from_user.id}",
|
||||||
|
tag = message.from_user.username,
|
||||||
|
name = message.from_user.first_name,
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|
||||||
def TelegramHandlerWrapper(update:telegram.Update, context:CallbackContext=None) -> None:
|
def TelegramHandlerWrapper(update:telegram.Update, context:CallbackContext=None) -> None:
|
||||||
Thread(target=lambda:TelegramHandlerCore(update, context)).start()
|
Thread(target=lambda:TelegramHandlerCore(update, context)).start()
|
||||||
|
|
||||||
def TelegramHandlerCore(update:telegram.Update, context:CallbackContext=None) -> None:
|
def TelegramHandlerCore(update:telegram.Update, context:CallbackContext=None) -> None:
|
||||||
if not update.message:
|
if not update.message:
|
||||||
return
|
return
|
||||||
data = SimpleNamespace()
|
data = TelegramMakeInputMessageData(update.message)
|
||||||
data.room_id = f"{update.message.chat.id}@telegram"
|
if update.message.reply_to_message:
|
||||||
data.message_id = f"{update.message.message_id}@telegram"
|
data.quoted = TelegramMakeInputMessageData(update.message.reply_to_message)
|
||||||
data.text_plain = update.message.text
|
|
||||||
data.text_markdown = update.message.text_markdown_v2
|
|
||||||
data.text_auto = GetWeightedText(data.text_markdown, data.text_plain)
|
|
||||||
data.command = ParseCommand(data.text_plain)
|
|
||||||
data.user = SimpleNamespace()
|
|
||||||
data.user.name = update.message.from_user.first_name
|
|
||||||
data.user.tag = update.message.from_user.username
|
|
||||||
data.user.id = f"{update.message.from_user.id}@telegram"
|
|
||||||
OnMessageParsed(data)
|
OnMessageParsed(data)
|
||||||
cmd = ParseCmd(update.message.text)
|
cmd = ParseCmd(update.message.text)
|
||||||
if cmd:
|
if cmd:
|
||||||
@ -59,7 +71,7 @@ def TelegramHandlerCore(update:telegram.Update, context:CallbackContext=None) ->
|
|||||||
cmd.User = SimpleNamespace(**{
|
cmd.User = SimpleNamespace(**{
|
||||||
"Name": update.message.from_user.first_name,
|
"Name": update.message.from_user.first_name,
|
||||||
"Tag": update.message.from_user.username,
|
"Tag": update.message.from_user.username,
|
||||||
"Id": f'{update.message.from_user.id}@telegram',
|
"Id": f'telegram:{update.message.from_user.id}',
|
||||||
})
|
})
|
||||||
if update.message.reply_to_message:
|
if update.message.reply_to_message:
|
||||||
cmd.Quoted = SimpleNamespace(**{
|
cmd.Quoted = SimpleNamespace(**{
|
||||||
@ -71,12 +83,13 @@ def TelegramHandlerCore(update:telegram.Update, context:CallbackContext=None) ->
|
|||||||
"User": SimpleNamespace(**{
|
"User": SimpleNamespace(**{
|
||||||
"Name": update.message.reply_to_message.from_user.first_name,
|
"Name": update.message.reply_to_message.from_user.first_name,
|
||||||
"Tag": update.message.reply_to_message.from_user.username,
|
"Tag": update.message.reply_to_message.from_user.username,
|
||||||
"Id": f'{update.message.reply_to_message.from_user.id}@telegram',
|
"Id": f'telegram:{update.message.reply_to_message.from_user.id}',
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
Endpoints[cmd.Name]["handler"]({"Event": update, "Manager": context}, cmd)
|
Endpoints[cmd.Name]["handler"]({"Event": update, "Manager": context}, cmd)
|
||||||
|
#Endpoints[cmd.Name]["handler"](SafeNamespace(platform="telegram", event=update, manager=context), cmd)
|
||||||
|
|
||||||
def TelegramSender(event, manager, data, destination, textPlain, textMarkdown) -> None:
|
def TelegramSender(event, manager, data:OutputMessageData, destination, textPlain, textMarkdown) -> None:
|
||||||
if destination:
|
if destination:
|
||||||
manager.bot.send_message(destination, text=textPlain)
|
manager.bot.send_message(destination, text=textPlain)
|
||||||
else:
|
else:
|
||||||
@ -84,30 +97,6 @@ def TelegramSender(event, manager, data, destination, textPlain, textMarkdown) -
|
|||||||
if InDict(data, "Media") and not InDict(data, "media"):
|
if InDict(data, "Media") and not InDict(data, "media"):
|
||||||
data["media"] = {"bytes": data["Media"]}
|
data["media"] = {"bytes": data["Media"]}
|
||||||
if InDict(data, "media"):
|
if InDict(data, "media"):
|
||||||
#data["media"] = SureArray(data["media"])
|
|
||||||
#media = (data["media"][0]["bytes"] if "bytes" in data["media"][0] else data["media"][0]["url"])
|
|
||||||
#if len(data["media"]) > 1:
|
|
||||||
# media_list = []
|
|
||||||
# media_list.append(telegram.InputMediaPhoto(
|
|
||||||
# media[0],
|
|
||||||
# caption=(textMarkdown if textMarkdown else textPlain if textPlain else None),
|
|
||||||
# parse_mode=("MarkdownV2" if textMarkdown else None)))
|
|
||||||
# for medium in media[1:]:
|
|
||||||
# media_list.append(telegram.InputMediaPhoto(medium))
|
|
||||||
# event.message.reply_media_group(media_list, reply_to_message_id=replyToId)
|
|
||||||
#else:
|
|
||||||
# event.message.reply_photo(
|
|
||||||
# media,
|
|
||||||
# caption=(textMarkdown if textMarkdown else textPlain if textPlain else None),
|
|
||||||
# parse_mode=("MarkdownV2" if textMarkdown else None),
|
|
||||||
# reply_to_message_id=replyToId)
|
|
||||||
#event.message.reply_photo(
|
|
||||||
# (DictGet(media[0], "bytes") or DictGet(media[0], "url")),
|
|
||||||
# caption=(textMarkdown if textMarkdown else textPlain if textPlain else None),
|
|
||||||
# parse_mode=("MarkdownV2" if textMarkdown else None),
|
|
||||||
# reply_to_message_id=replyToId)
|
|
||||||
#for medium in media[1:]:
|
|
||||||
# event.message.reply_photo((DictGet(medium, "bytes") or DictGet(medium, "url")), reply_to_message_id=replyToId)
|
|
||||||
for medium in SureArray(data["media"]):
|
for medium in SureArray(data["media"]):
|
||||||
event.message.reply_photo(
|
event.message.reply_photo(
|
||||||
(DictGet(medium, "bytes") or DictGet(medium, "url")),
|
(DictGet(medium, "bytes") or DictGet(medium, "url")),
|
||||||
@ -119,5 +108,15 @@ def TelegramSender(event, manager, data, destination, textPlain, textMarkdown) -
|
|||||||
elif textPlain:
|
elif textPlain:
|
||||||
event.message.reply_text(textPlain, reply_to_message_id=replyToId)
|
event.message.reply_text(textPlain, reply_to_message_id=replyToId)
|
||||||
|
|
||||||
RegisterPlatform(name="Telegram", main=TelegramMain, sender=TelegramSender, eventClass=telegram.Update)
|
def TelegramLinker(data:InputMessageData) -> SafeNamespace:
|
||||||
|
linked = SafeNamespace()
|
||||||
|
if data.room.id:
|
||||||
|
room_id = data.room.id.split("telegram:")[1]
|
||||||
|
linked.room = f"https://t.me/{room_id}"
|
||||||
|
if data.message_id:
|
||||||
|
message_id = data.message_id.split("telegram:")[1]
|
||||||
|
linked.message = f"{linked.room}/{message_id}"
|
||||||
|
return linked
|
||||||
|
|
||||||
|
RegisterPlatform(name="Telegram", main=TelegramMain, sender=TelegramSender, linker=TelegramLinker, eventClass=telegram.Update)
|
||||||
|
|
||||||
|
20
ModWinDog/Broadcast.py
Executable file
20
ModWinDog/Broadcast.py
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
# ================================== #
|
||||||
|
# WinDog multi-purpose chatbot #
|
||||||
|
# Licensed under AGPLv3 by OctoSpacc #
|
||||||
|
# ================================== #
|
||||||
|
|
||||||
|
def cBroadcast(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
if (data.user.id not in AdminIds) and (data.user.tag not in AdminIds):
|
||||||
|
return SendMessage(context, {"Text": choice(Locale.__('eval'))})
|
||||||
|
destination = data.command.arguments["destination"]
|
||||||
|
if not (destination and data.command.body):
|
||||||
|
return SendMessage(context, {"Text": "Bad usage."})
|
||||||
|
SendMessage(context, {"TextPlain": data.command.body}, destination)
|
||||||
|
SendMessage(context, {"TextPlain": "Executed."})
|
||||||
|
|
||||||
|
RegisterModule(name="Broadcast", endpoints={
|
||||||
|
"Broadcast": CreateEndpoint(["broadcast"], summary="Sends an admin message over to any chat destination.", handler=cBroadcast, arguments={
|
||||||
|
"destination": True,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
23
ModWinDog/ChatGPT/ChatGPT.py
Normal file
23
ModWinDog/ChatGPT/ChatGPT.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# ================================== #
|
||||||
|
# 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:
|
||||||
|
if not data.command.body:
|
||||||
|
return SendMessage(context, {"Text": "You must type some text."})
|
||||||
|
output = ""
|
||||||
|
while not output or output.startswith("sorry, 您的ip已由于触发防滥用检测而被封禁,本服务网址是"): # quick fix
|
||||||
|
output = ""
|
||||||
|
for completion in g4fClient.chat.completions.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": data.command.body}], stream=True):
|
||||||
|
output += (completion.choices[0].delta.content or "")
|
||||||
|
return SendMessage(context, {"TextPlain": f"[🤖️ GPT]\n\n{output}"})
|
||||||
|
|
||||||
|
RegisterModule(name="ChatGPT", endpoints={
|
||||||
|
"GPT": CreateEndpoint(["gpt", "chatgpt"], summary="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!", handler=cGpt),
|
||||||
|
})
|
||||||
|
|
1
ModWinDog/ChatGPT/requirements.txt
Normal file
1
ModWinDog/ChatGPT/requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
g4f
|
@ -5,16 +5,16 @@
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
def cHash(context, data) -> None:
|
def cHash(context:EventContext, data:InputMessageData) -> None:
|
||||||
algorithm = data.command.arguments["algorithm"]
|
algorithm = data.command.arguments["algorithm"]
|
||||||
if data.command.body and algorithm in hashlib.algorithms_available:
|
if data.command.body and algorithm in hashlib.algorithms_available:
|
||||||
hashed = hashlib.new(algorithm, algorithm.join(data.Body.split(algorithm)[1:]).strip().encode()).hexdigest()
|
hashed = hashlib.new(algorithm, data.command.body.encode()).hexdigest()
|
||||||
SendMsg(context, {
|
SendMessage(context, {
|
||||||
"TextPlain": hashed,
|
"TextPlain": hashed,
|
||||||
"TextMarkdown": MarkdownCode(hashed, True),
|
"TextMarkdown": MarkdownCode(hashed, True),
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
SendMsg(context, {"Text": choice(Locale.__('hash.usage')).format(data.command.tokens[0], hashlib.algorithms_available)})
|
SendMessage(context, {"Text": choice(Locale.__('hash.usage')).format(data.command.tokens[0], hashlib.algorithms_available)})
|
||||||
|
|
||||||
RegisterModule(name="Hashing", group="Geek", summary="Functions for hashing of textual content.", endpoints={
|
RegisterModule(name="Hashing", group="Geek", summary="Functions for hashing of textual content.", endpoints={
|
||||||
"Hash": CreateEndpoint(names=["hash"], summary="Responds with the hash-sum of a message received.", handler=cHash, arguments={
|
"Hash": CreateEndpoint(names=["hash"], summary="Responds with the hash-sum of a message received.", handler=cHash, arguments={
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
# ================================== #
|
# ================================== #
|
||||||
|
|
||||||
# TODO: implement /help <commandname> feature
|
# TODO: implement /help <commandname> feature
|
||||||
def cHelp(context, data=None) -> None:
|
def cHelp(context:EventContext, data:InputMessageData) -> None:
|
||||||
moduleList = ''
|
moduleList = ''
|
||||||
for module in Modules:
|
for module in Modules:
|
||||||
summary = Modules[module]["summary"]
|
summary = Modules[module]["summary"]
|
||||||
@ -13,7 +13,7 @@ def cHelp(context, data=None) -> None:
|
|||||||
for endpoint in endpoints:
|
for endpoint in endpoints:
|
||||||
summary = endpoints[endpoint]["summary"]
|
summary = endpoints[endpoint]["summary"]
|
||||||
moduleList += (f"\n* /{', /'.join(endpoints[endpoint]['names'])}" + (f": {summary}" if summary else ''))
|
moduleList += (f"\n* /{', /'.join(endpoints[endpoint]['names'])}" + (f": {summary}" if summary else ''))
|
||||||
SendMsg(context, {"Text": f"[ Available Modules ]{moduleList}"})
|
SendMessage(context, {"Text": f"[ Available Modules ]{moduleList}"})
|
||||||
|
|
||||||
RegisterModule(name="Help", group="Basic", endpoints={
|
RegisterModule(name="Help", group="Basic", endpoints={
|
||||||
"Help": CreateEndpoint(["help"], summary="Provides help for the bot. For now, it just lists the commands.", handler=cHelp),
|
"Help": CreateEndpoint(["help"], summary="Provides help for the bot. For now, it just lists the commands.", handler=cHelp),
|
||||||
|
@ -16,7 +16,7 @@ from urllib.request import urlopen, Request
|
|||||||
def HttpReq(url:str, method:str|None=None, *, body:bytes=None, headers:dict[str, str]={"User-Agent": WebUserAgent}):
|
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))
|
return urlopen(Request(url, method=method, data=body, headers=headers))
|
||||||
|
|
||||||
def cEmbedded(context, data) -> None:
|
def cEmbedded(context:EventContext, data:InputMessageData) -> None:
|
||||||
if len(data.Tokens) >= 2:
|
if len(data.Tokens) >= 2:
|
||||||
# Find links in command body
|
# Find links in command body
|
||||||
Text = (data.TextMarkdown + ' ' + data.TextPlain)
|
Text = (data.TextMarkdown + ' ' + data.TextPlain)
|
||||||
@ -48,10 +48,10 @@ def cEmbedded(context, data) -> None:
|
|||||||
elif urlDomain == "vm.tiktok.com":
|
elif urlDomain == "vm.tiktok.com":
|
||||||
urlDomain = "vm.vxtiktok.com"
|
urlDomain = "vm.vxtiktok.com"
|
||||||
url = urlDomain + url[len(urlDomain):]
|
url = urlDomain + url[len(urlDomain):]
|
||||||
SendMsg(context, {"TextPlain": f"{{{proto}{url}}}"})
|
SendMessage(context, {"TextPlain": f"{{{proto}{url}}}"})
|
||||||
# else TODO error message?
|
# else TODO error message?
|
||||||
|
|
||||||
def cWeb(context, data) -> None:
|
def cWeb(context:EventContext, data:InputMessageData) -> None:
|
||||||
if data.Body:
|
if data.Body:
|
||||||
try:
|
try:
|
||||||
QueryUrl = UrlParse.quote(data.Body)
|
QueryUrl = UrlParse.quote(data.Body)
|
||||||
@ -68,34 +68,35 @@ def cWeb(context, data) -> None:
|
|||||||
Caption += f'[{Index}] {Title} : {{{Link}}}\n\n'
|
Caption += f'[{Index}] {Title} : {{{Link}}}\n\n'
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
SendMsg(context, {"TextPlain": f'{Caption}...'})
|
SendMessage(context, {"TextPlain": f'{Caption}...'})
|
||||||
except Exception:
|
except Exception:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def cImages(context, data) -> None:
|
def cImages(context:EventContext, data:InputMessageData) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def cNews(context, data) -> None:
|
def cNews(context:EventContext, data:InputMessageData) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def cTranslate(context, data) -> None:
|
def cTranslate(context:EventContext, data:InputMessageData) -> None:
|
||||||
if len(data.Tokens) < 3:
|
language_to = data.command.arguments["language_to"]
|
||||||
return
|
text_input = (data.command.body or (data.Quoted and data.Quoted.Body))
|
||||||
|
if not (text_input and language_to):
|
||||||
|
return SendMessage(context, {"TextPlain": f"Usage: /translate <to language> <text>"})
|
||||||
try:
|
try:
|
||||||
toLang = data.Tokens[1]
|
|
||||||
# TODO: Use many different public Lingva instances in rotation to avoid overloading a specific one
|
# TODO: Use many different public Lingva instances in rotation to avoid overloading a specific one
|
||||||
result = json.loads(HttpReq(f'https://lingva.ml/api/v1/auto/{toLang}/{UrlParse.quote(toLang.join(data.Body.split(toLang)[1:]))}').read())
|
result = json.loads(HttpReq(f'https://lingva.ml/api/v1/auto/{language_to}/{UrlParse.quote(text_input)}').read())
|
||||||
SendMsg(context, {"TextPlain": f"[{result['info']['detectedSource']} (auto) -> {toLang}]\n\n{result['translation']}"})
|
SendMessage(context, {"TextPlain": f"[{result['info']['detectedSource']} (auto) -> {language_to}]\n\n{result['translation']}"})
|
||||||
except Exception:
|
except Exception:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def cUnsplash(context, data) -> None:
|
def cUnsplash(context:EventContext, data:InputMessageData) -> None:
|
||||||
try:
|
try:
|
||||||
Req = HttpReq(f'https://source.unsplash.com/random/?{UrlParse.quote(data.Body)}')
|
Req = HttpReq(f'https://source.unsplash.com/random/?{UrlParse.quote(data.Body)}')
|
||||||
ImgUrl = Req.geturl().split('?')[0]
|
ImgUrl = Req.geturl().split('?')[0]
|
||||||
SendMsg(context, {
|
SendMessage(context, {
|
||||||
"TextPlain": f'{{{ImgUrl}}}',
|
"TextPlain": f'{{{ImgUrl}}}',
|
||||||
"TextMarkdown": MarkdownCode(ImgUrl, True),
|
"TextMarkdown": MarkdownCode(ImgUrl, True),
|
||||||
"Media": Req.read(),
|
"Media": Req.read(),
|
||||||
@ -103,7 +104,7 @@ def cUnsplash(context, data) -> None:
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def cSafebooru(context, data) -> None:
|
def cSafebooru(context:EventContext, data:InputMessageData) -> None:
|
||||||
ApiUrl = 'https://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags='
|
ApiUrl = 'https://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags='
|
||||||
try:
|
try:
|
||||||
if data.Body:
|
if data.Body:
|
||||||
@ -114,7 +115,7 @@ def cSafebooru(context, data) -> None:
|
|||||||
if not ImgUrls: # literal search
|
if not ImgUrls: # literal search
|
||||||
ImgUrls = HttpReq(f'{ApiUrl}{UrlParse.quote(data.Body)}').read().decode().split(' file_url="')[1:]
|
ImgUrls = HttpReq(f'{ApiUrl}{UrlParse.quote(data.Body)}').read().decode().split(' file_url="')[1:]
|
||||||
if not ImgUrls:
|
if not ImgUrls:
|
||||||
return SendMsg(context, {"Text": "Error: Could not get any result from Safebooru."})
|
return SendMessage(context, {"Text": "Error: Could not get any result from Safebooru."})
|
||||||
ImgXml = choice(ImgUrls)
|
ImgXml = choice(ImgUrls)
|
||||||
ImgUrl = ImgXml.split('"')[0]
|
ImgUrl = ImgXml.split('"')[0]
|
||||||
ImgId = ImgXml.split(' id="')[1].split('"')[0]
|
ImgId = ImgXml.split(' id="')[1].split('"')[0]
|
||||||
@ -126,7 +127,7 @@ def cSafebooru(context, data) -> None:
|
|||||||
ImgId = ImgUrl.split('?')[-1]
|
ImgId = ImgUrl.split('?')[-1]
|
||||||
break
|
break
|
||||||
if ImgUrl:
|
if ImgUrl:
|
||||||
SendMsg(context, {
|
SendMessage(context, {
|
||||||
"TextPlain": f'[{ImgId}]\n{{{ImgUrl}}}',
|
"TextPlain": f'[{ImgId}]\n{{{ImgUrl}}}',
|
||||||
"TextMarkdown": (f'\\[`{ImgId}`\\]\n' + MarkdownCode(ImgUrl, True)),
|
"TextMarkdown": (f'\\[`{ImgId}`\\]\n' + MarkdownCode(ImgUrl, True)),
|
||||||
"media": {"url": ImgUrl}, #, "bytes": HttpReq(ImgUrl).read()},
|
"media": {"url": ImgUrl}, #, "bytes": HttpReq(ImgUrl).read()},
|
||||||
@ -136,45 +137,14 @@ def cSafebooru(context, data) -> None:
|
|||||||
except Exception as error:
|
except Exception as error:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def cDalle(context, data) -> None:
|
|
||||||
if not data.Body:
|
|
||||||
return SendMsg(context, {"Text": "Please tell me what to generate."})
|
|
||||||
image_filter = ""https://th.bing.com/th/id/"
|
|
||||||
try:
|
|
||||||
retry_index = 3
|
|
||||||
result_list = ""
|
|
||||||
result_id = HttpReq(
|
|
||||||
f"https://www.bing.com/images/create?q={UrlParse.quote(data.Body)}&rt=3&FORM=GENCRE",#"4&FORM=GENCRE",
|
|
||||||
body=f"q={UrlParse.urlencode({'q': data.Body})}&qs=ds".encode(),
|
|
||||||
headers=MicrosoftBingSettings).read().decode()
|
|
||||||
print(result_id)
|
|
||||||
result_id = result_id.split('&id=')[1].split('&')[0]
|
|
||||||
results_url = f"https://www.bing.com/images/create/-/{result_id}?FORM=GENCRE"
|
|
||||||
SendMsg(context, {"Text": "Request sent, please wait..."})
|
|
||||||
while retry_index < 12 and image_filter not in result_list:
|
|
||||||
result_list = HttpReq(results_url, headers={"User-Agent": MicrosoftBingSettings["User-Agent"]}).read().decode()
|
|
||||||
time.sleep(1.25 * retry_index)
|
|
||||||
retry_index += 1
|
|
||||||
if image_filter in result_list:
|
|
||||||
SendMsg(context, {
|
|
||||||
"TextPlain": f"{{{results_url}}}",
|
|
||||||
"TextMarkdown": MarkdownCode(results_url, True),
|
|
||||||
"Media": HttpReq(
|
|
||||||
result_list.split(image_filter)[1].split('\\"')[0],
|
|
||||||
headers={"User-Agent": MicrosoftBingSettings["User-Agent"]}).read(),
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
raise Exception("Something went wrong.")
|
|
||||||
except Exception as error:
|
|
||||||
Log(error)
|
|
||||||
SendMsg(context, {"TextPlain": error})
|
|
||||||
|
|
||||||
RegisterModule(name="Internet", summary="Tools and toys related to the Internet.", endpoints={
|
RegisterModule(name="Internet", summary="Tools and toys related to the Internet.", endpoints={
|
||||||
"Embedded": CreateEndpoint(["embedded"], summary="Rewrites a link, trying to bypass embed view protection.", handler=cEmbedded),
|
"Embedded": CreateEndpoint(["embedded"], summary="Rewrites a link, trying to bypass embed view protection.", handler=cEmbedded),
|
||||||
"Web": CreateEndpoint(["web"], summary="Provides results of a DuckDuckGo search.", handler=cWeb),
|
"Web": CreateEndpoint(["web"], summary="Provides results of a DuckDuckGo search.", handler=cWeb),
|
||||||
"Translate": CreateEndpoint(["translate"], summary="Returns the received message after translating it in another language.", handler=cTranslate),
|
"Translate": CreateEndpoint(["translate"], summary="Returns the received message after translating it in another language.", handler=cTranslate, arguments={
|
||||||
|
"language_to": True,
|
||||||
|
"language_from": False,
|
||||||
|
}),
|
||||||
"Unsplash": CreateEndpoint(["unsplash"], summary="Sends a picture sourced from Unsplash.", handler=cUnsplash),
|
"Unsplash": CreateEndpoint(["unsplash"], summary="Sends a picture sourced from Unsplash.", handler=cUnsplash),
|
||||||
"Safebooru": CreateEndpoint(["safebooru"], summary="Sends a picture sourced from Safebooru.", handler=cSafebooru),
|
"Safebooru": CreateEndpoint(["safebooru"], summary="Sends a picture sourced from Safebooru.", handler=cSafebooru),
|
||||||
#"DALL-E": CreateEndpoint(["dalle"], summary="Sends an AI-generated picture from DALL-E 3 via Microsoft Bing.", handler=cDalle),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
|
|
||||||
import re, subprocess
|
import re, subprocess
|
||||||
|
|
||||||
def mPercenter(context, data) -> None:
|
def mPercenter(context:EventContext, data:InputMessageData) -> None:
|
||||||
SendMsg(context, {"Text": choice(Locale.__(f'{data.Name}.{"done" if data.Body else "empty"}')).format(
|
SendMessage(context, {"Text": choice(Locale.__(f'{data.Name}.{"done" if data.Body else "empty"}')).format(
|
||||||
Cmd=data.Tokens[0], Percent=RandPercent(), Thing=data.Body)})
|
Cmd=data.Tokens[0], Percent=RandPercent(), Thing=data.Body)})
|
||||||
|
|
||||||
def mMultifun(context, data) -> None:
|
def mMultifun(context:EventContext, data:InputMessageData) -> None:
|
||||||
cmdkey = data.Name
|
cmdkey = data.Name
|
||||||
replyToId = None
|
replyToId = None
|
||||||
if data.Quoted:
|
if data.Quoted:
|
||||||
@ -26,17 +26,20 @@ def mMultifun(context, data) -> None:
|
|||||||
else:
|
else:
|
||||||
if 'empty' in Locale.__(cmdkey):
|
if 'empty' in Locale.__(cmdkey):
|
||||||
Text = choice(Locale.__(f'{cmdkey}.empty'))
|
Text = choice(Locale.__(f'{cmdkey}.empty'))
|
||||||
SendMsg(context, {"Text": Text, "ReplyTo": replyToId})
|
SendMessage(context, {"Text": Text, "ReplyTo": replyToId})
|
||||||
|
|
||||||
def cStart(context, data) -> None:
|
def cStart(context:EventContext, data:InputMessageData) -> None:
|
||||||
SendMsg(context, {"Text": choice(Locale.__('start')).format(data.User.Name)})
|
SendMessage(context, {"Text": choice(Locale.__('start')).format(data.User.Name)})
|
||||||
|
|
||||||
def cSource(context, data=None) -> None:
|
def cSource(context:EventContext, data:InputMessageData) -> None:
|
||||||
SendMsg(context, {"TextPlain": ("""\
|
SendMessage(context, {"TextPlain": ("""\
|
||||||
* Original Code: {https://gitlab.com/octospacc/WinDog}
|
* Original Code: {https://gitlab.com/octospacc/WinDog}
|
||||||
* Mirror: {https://github.com/octospacc/WinDog}
|
* Mirror: {https://github.com/octospacc/WinDog}
|
||||||
""" + (f"* Modified Code: {{{ModifiedSourceUrl}}}" if ModifiedSourceUrl else ""))})
|
""" + (f"* Modified Code: {{{ModifiedSourceUrl}}}" if ModifiedSourceUrl else ""))})
|
||||||
|
|
||||||
|
def cGdpr(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
# Module: Config
|
# Module: Config
|
||||||
# ...
|
# ...
|
||||||
#def cConfig(update:telegram.Update, context:CallbackContext) -> None:
|
#def cConfig(update:telegram.Update, context:CallbackContext) -> None:
|
||||||
@ -46,12 +49,13 @@ def cSource(context, data=None) -> None:
|
|||||||
# # ... language: en, it, ...
|
# # ... language: en, it, ...
|
||||||
# # ... userdata: import, export, delete
|
# # ... userdata: import, export, delete
|
||||||
|
|
||||||
def cPing(context, data=None) -> None:
|
def cPing(context:EventContext, data:InputMessageData) -> None:
|
||||||
SendMsg(context, {"Text": "*Pong!*"})
|
SendMessage(context, {"Text": "*Pong!*"})
|
||||||
|
|
||||||
def cEcho(context, data) -> None:
|
def cEcho(context:EventContext, data:InputMessageData) -> None:
|
||||||
if data.Body:
|
if data.command.body:
|
||||||
prefix = "🗣️ "
|
prefix = "🗣️ "
|
||||||
|
#prefix = f"[🗣️]({context.linker(data).message}) "
|
||||||
if len(data.Tokens) == 2:
|
if len(data.Tokens) == 2:
|
||||||
nonascii = True
|
nonascii = True
|
||||||
for char in data.Tokens[1]:
|
for char in data.Tokens[1]:
|
||||||
@ -61,50 +65,40 @@ def cEcho(context, data) -> None:
|
|||||||
if nonascii:
|
if nonascii:
|
||||||
# text is not ascii, probably an emoji (altough not necessarily), so just pass as is (useful for Telegram emojis)
|
# text is not ascii, probably an emoji (altough not necessarily), so just pass as is (useful for Telegram emojis)
|
||||||
prefix = ''
|
prefix = ''
|
||||||
SendMsg(context, {"Text": (prefix + data.Body)})
|
SendMessage(context, {"Text": (prefix + data.command.body)})
|
||||||
else:
|
else:
|
||||||
SendMsg(context, {"Text": choice(Locale.__('echo.empty'))})
|
SendMessage(context, {"Text": choice(Locale.__('echo.empty'))})
|
||||||
|
|
||||||
def cBroadcast(context, data) -> None:
|
|
||||||
if data.User.Id not in AdminIds:
|
|
||||||
return SendMsg(context, {"Text": choice(Locale.__('eval'))})
|
|
||||||
if len(data.Tokens) < 3:
|
|
||||||
return SendMsg(context, {"Text": "Bad usage."})
|
|
||||||
Dest = data.Tokens[1]
|
|
||||||
Text = ' '.join(data.Tokens[2:])
|
|
||||||
SendMsg(context, {"TextPlain": Text}, Dest)
|
|
||||||
SendMsg(context, {"TextPlain": "Executed."})
|
|
||||||
|
|
||||||
#def cTime(update:Update, context:CallbackContext) -> None:
|
#def cTime(update:Update, context:CallbackContext) -> None:
|
||||||
# update.message.reply_markdown_v2(
|
# update.message.reply_markdown_v2(
|
||||||
# CharEscape(choice(Locale.__('time')).format(time.ctime().replace(' ', ' ')), 'MARKDOWN_SPEECH'),
|
# CharEscape(choice(Locale.__('time')).format(time.ctime().replace(' ', ' ')), 'MARKDOWN_SPEECH'),
|
||||||
# reply_to_message_id=update.message.message_id)
|
# reply_to_message_id=update.message.message_id)
|
||||||
|
|
||||||
def cEval(context, data=None) -> None:
|
def cEval(context:EventContext, data:InputMessageData) -> None:
|
||||||
SendMsg(context, {"Text": choice(Locale.__('eval'))})
|
SendMessage(context, {"Text": choice(Locale.__('eval'))})
|
||||||
|
|
||||||
def cExec(context, data) -> None:
|
def cExec(context:EventContext, data:InputMessageData) -> None:
|
||||||
if len(data.Tokens) >= 2 and data.Tokens[1].lower() in ExecAllowed:
|
if len(data.Tokens) >= 2 and data.Tokens[1].lower() in ExecAllowed:
|
||||||
cmd = data.Tokens[1].lower()
|
cmd = data.Tokens[1].lower()
|
||||||
Out = subprocess.run(('sh', '-c', f'export PATH=$PATH:/usr/games; {cmd}'),
|
Out = subprocess.run(('sh', '-c', f'export PATH=$PATH:/usr/games; {cmd}'),
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode()
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode()
|
||||||
# <https://stackoverflow.com/a/14693789>
|
# <https://stackoverflow.com/a/14693789>
|
||||||
Caption = (re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])').sub('', Out))
|
Caption = (re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])').sub('', Out))
|
||||||
SendMsg(context, {
|
SendMessage(context, {
|
||||||
"TextPlain": Caption,
|
"TextPlain": Caption,
|
||||||
"TextMarkdown": MarkdownCode(Caption, True),
|
"TextMarkdown": MarkdownCode(Caption, True),
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
SendMsg(context, {"Text": choice(Locale.__('eval'))})
|
SendMessage(context, {"Text": choice(Locale.__('eval'))})
|
||||||
|
|
||||||
RegisterModule(name="Misc", endpoints={
|
RegisterModule(name="Misc", endpoints={
|
||||||
"Percenter": CreateEndpoint(["wish", "level"], summary="Provides fun trough percentage-based toys.", handler=mPercenter),
|
"Percenter": CreateEndpoint(["wish", "level"], summary="Provides fun trough percentage-based toys.", handler=mPercenter),
|
||||||
"Multifun": CreateEndpoint(["hug", "pat", "poke", "cuddle", "hands", "floor", "sessocto"], summary="Provides fun trough preprogrammed-text-based toys.", handler=mMultifun),
|
"Multifun": CreateEndpoint(["hug", "pat", "poke", "cuddle", "hands", "floor", "sessocto"], summary="Provides fun trough preprogrammed-text-based toys.", handler=mMultifun),
|
||||||
"Start": CreateEndpoint(["start"], summary="Salutes the user, hinting that the bot is working and providing basic quick help.", handler=cStart),
|
"Start": CreateEndpoint(["start"], summary="Salutes the user, hinting that the bot is working and providing basic quick help.", handler=cStart),
|
||||||
"Source": CreateEndpoint(["source"], summary="Provides a copy of the bot source codes and/or instructions on how to get it.", handler=cSource),
|
"Source": CreateEndpoint(["source"], summary="Provides a copy of the bot source codes and/or instructions on how to get it.", handler=cSource),
|
||||||
|
"GDPR": CreateEndpoint(["gdpr"], summary="Operations for european citizens regarding your personal data.", handler=cGdpr),
|
||||||
"Ping": CreateEndpoint(["ping"], summary="Responds pong, useful for testing messaging latency.", handler=cPing),
|
"Ping": CreateEndpoint(["ping"], summary="Responds pong, useful for testing messaging latency.", handler=cPing),
|
||||||
"Echo": CreateEndpoint(["echo"], summary="Responds back with the original text of the received message.", handler=cEcho),
|
"Echo": CreateEndpoint(["echo"], summary="Responds back with the original text of the received message.", handler=cEcho),
|
||||||
"Broadcast": CreateEndpoint(["broadcast"], summary="Sends an admin message over to any chat destination.", handler=cBroadcast),
|
|
||||||
"Eval": CreateEndpoint(["eval"], summary="Execute a Python command (or safe literal operation) in the current context. Currently not implemented.", handler=cEval),
|
"Eval": CreateEndpoint(["eval"], summary="Execute a Python command (or safe literal operation) in the current context. Currently not implemented.", handler=cEval),
|
||||||
"Exec": CreateEndpoint(["exec"], summary="Execute a system command from the allowed ones and return stdout+stderr.", handler=cExec),
|
"Exec": CreateEndpoint(["exec"], summary="Execute a system command from the allowed ones and return stdout+stderr.", handler=cExec),
|
||||||
#"Format": CreateEndpoint(["format"], summary="Reformat text using an handful of rules. Not yet implemented.", handler=cFormat),
|
#"Format": CreateEndpoint(["format"], summary="Reformat text using an handful of rules. Not yet implemented.", handler=cFormat),
|
||||||
|
@ -9,94 +9,120 @@ SeleniumDriversLimit = 2
|
|||||||
|
|
||||||
""" # end windog config # """
|
""" # end windog config # """
|
||||||
|
|
||||||
currentSeleniumDrivers = 0
|
currentSeleniumDrivers = []
|
||||||
|
|
||||||
#from selenium import webdriver
|
|
||||||
#from selenium.webdriver import Chrome
|
|
||||||
#from selenium.webdriver.common.by import By
|
|
||||||
from seleniumbase import Driver
|
from seleniumbase import Driver
|
||||||
|
|
||||||
def getSelenium() -> Driver:
|
# TODO implement some kind of timeout after a closure of a browser, since otherwise we get in a buggy state sometimes?
|
||||||
global currentSeleniumDrivers
|
|
||||||
if currentSeleniumDrivers >= SeleniumDriversLimit:
|
def getSelenium() -> tuple[int, Driver]|bool:
|
||||||
|
if len(currentSeleniumDrivers) == SeleniumDriversLimit:
|
||||||
return False
|
return False
|
||||||
#options = webdriver.ChromeOptions()
|
for index in range(1, (SeleniumDriversLimit + 1)):
|
||||||
#options.add_argument("headless=new")
|
if index not in currentSeleniumDrivers:
|
||||||
#options.add_argument("user-data-dir=./Selenium-WinDog")
|
currentSeleniumDrivers.append(index)
|
||||||
#seleniumDriver = Chrome(options=options)
|
break
|
||||||
currentSeleniumDrivers += 1
|
return (index, Driver(uc=True, headless2=True, user_data_dir=f"./Selenium-WinDog/{index}"))
|
||||||
return Driver(uc=True, headless2=True, user_data_dir=f"./Selenium-WinDog/{currentSeleniumDrivers}")
|
|
||||||
|
|
||||||
def closeSelenium(driver:Driver) -> None:
|
def closeSelenium(index:int, driver:Driver) -> None:
|
||||||
global currentSeleniumDrivers
|
if driver:
|
||||||
try:
|
try:
|
||||||
driver.close()
|
driver.close()
|
||||||
driver.quit()
|
driver.quit()
|
||||||
except:
|
except:
|
||||||
Log(format_exc())
|
Log(format_exc())
|
||||||
if currentSeleniumDrivers > 0:
|
if index:
|
||||||
currentSeleniumDrivers -= 1
|
currentSeleniumDrivers.remove(index)
|
||||||
|
|
||||||
def cDalleSelenium(context, data) -> None:
|
def cDalleSelenium(context:EventContext, data:InputMessageData) -> None:
|
||||||
if not data.Body:
|
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."
|
||||||
return SendMsg(context, {"Text": "Please tell me what to generate."})
|
prompt = data.command.body
|
||||||
#if not seleniumDriver:
|
if not prompt:
|
||||||
# SendMsg(context, {"Text": "Initializing Selenium, please wait..."})
|
return SendMessage(context, {"Text": "Please tell me what to generate."})
|
||||||
# loadSeleniumDriver()
|
driver_index, driver = None, None
|
||||||
try:
|
try:
|
||||||
driver = getSelenium()
|
driver = getSelenium()
|
||||||
if not driver:
|
if not driver:
|
||||||
return SendMsg(context, {"Text": "Couldn't access a web scraping VM as they are all busy. Please try again later."})
|
return SendMessage(context, {"Text": "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.get("https://www.bing.com/images/create/")
|
||||||
driver.refresh()
|
driver.refresh()
|
||||||
#retry_index = 3
|
driver.find_element('form input[name="q"]').send_keys(prompt)
|
||||||
#while retry_index < 12:
|
|
||||||
# time.sleep(retry_index := retry_index + 1)
|
|
||||||
# try:
|
|
||||||
#seleniumDriver.find_element(By.CSS_SELECTOR, 'form input[name="q"]').send_keys(data.Body)
|
|
||||||
#seleniumDriver.find_element(By.CSS_SELECTOR, 'form a[role="button"]').submit()
|
|
||||||
driver.find_element('form input[name="q"]').send_keys(data.Body)
|
|
||||||
driver.find_element('form a[role="button"]').submit()
|
driver.find_element('form a[role="button"]').submit()
|
||||||
try:
|
try:
|
||||||
driver.find_element('img[alt="Content warning"]')
|
driver.find_element('img.gil_err_img[alt="Content warning"]')
|
||||||
SendMsg(context, {"Text": "This prompt has been blocked by Microsoft because it violates their content policy. Further attempts might lead to a ban on your profile."})
|
SendMessage(context, {"Text": f"Content warning: This prompt {warning_text}", "media": {"bytes": open("./Assets/ImageCreator-CodeOfConduct.png", 'rb').read()}})
|
||||||
closeSelenium(driver)
|
return closeSelenium(driver_index, driver)
|
||||||
return
|
|
||||||
except Exception: # warning element was not found, we should be good
|
except Exception: # warning element was not found, we should be good
|
||||||
pass
|
pass
|
||||||
SendMsg(context, {"Text": "Request sent successfully, please wait..."})
|
SendMessage(context, {"Text": "Request sent successfully, please wait..."})
|
||||||
# except Exception:
|
|
||||||
# pass
|
|
||||||
retry_index = 3
|
retry_index = 3
|
||||||
while retry_index < 12:
|
while retry_index < 12:
|
||||||
# note that sometimes generation fails and we will never get any image!
|
# note that sometimes generation can still fail and we will never get any image!
|
||||||
#try:
|
|
||||||
time.sleep(retry_index := retry_index + 1)
|
time.sleep(retry_index := retry_index + 1)
|
||||||
driver.refresh()
|
driver.refresh()
|
||||||
img_list = driver.find_elements(#By.CSS_SELECTOR,
|
img_list = driver.find_elements('div.imgpt a img.mimg')
|
||||||
'div.imgpt a img.mimg')
|
|
||||||
if not len(img_list):
|
if not len(img_list):
|
||||||
continue
|
try:
|
||||||
|
driver.find_element('img.gil_err_img[alt="Unsafe image content detected"]')
|
||||||
|
SendMessage(context, {"Text": "Unsafe image content detected: This result {warning_text}", "media": {"bytes": open("./Assets/ImageCreator-CodeOfConduct.png", 'rb').read()}})
|
||||||
|
return closeSelenium(driver_index, driver)
|
||||||
|
except: # no error is present, so we just have to wait more for the images
|
||||||
|
continue
|
||||||
img_array = []
|
img_array = []
|
||||||
for img_url in img_list:
|
for img_url in img_list:
|
||||||
img_url = img_url.get_attribute("src").split('?')[0]
|
img_url = img_url.get_attribute("src").split('?')[0]
|
||||||
img_array.append({"url": img_url}) #, "bytes": HttpReq(img_url).read()})
|
img_array.append({"url": img_url}) #, "bytes": HttpReq(img_url).read()})
|
||||||
page_url = driver.current_url.split('?')[0]
|
page_url = driver.current_url.split('?')[0]
|
||||||
SendMsg(context, {
|
SendMessage(context, {
|
||||||
"TextPlain": f'"{data.Body}"\n{{{page_url}}}',
|
"TextPlain": f'"{prompt}"\n{{{page_url}}}',
|
||||||
"TextMarkdown": (f'"_{CharEscape(data.Body, "MARKDOWN")}_"\n' + MarkdownCode(page_url, True)),
|
"TextMarkdown": (f'"_{CharEscape(prompt, "MARKDOWN")}_"\n' + MarkdownCode(page_url, True)),
|
||||||
"media": img_array,
|
"media": img_array,
|
||||||
})
|
})
|
||||||
closeSelenium(driver)
|
return closeSelenium(driver_index, driver)
|
||||||
break
|
raise Exception("VM timed out.")
|
||||||
#except Exception as ex:
|
|
||||||
# pass
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
Log(format_exc())
|
Log(format_exc())
|
||||||
SendMsg(context, {"TextPlain": "An unexpected error occurred."})
|
SendMessage(context, {"TextPlain": "An unexpected error occurred."})
|
||||||
closeSelenium(driver)
|
closeSelenium(driver_index, driver)
|
||||||
|
|
||||||
|
def cCraiyonSelenium(context:EventContext, data:InputMessageData) -> None:
|
||||||
|
prompt = data.command.body
|
||||||
|
if not prompt:
|
||||||
|
return SendMessage(context, {"Text": "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."})
|
||||||
|
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..."})
|
||||||
|
retry_index = 3
|
||||||
|
while retry_index < 16:
|
||||||
|
time.sleep(retry_index := retry_index + 1)
|
||||||
|
img_list = driver.find_elements('div.image-container > img')
|
||||||
|
if not len(img_list):
|
||||||
|
continue
|
||||||
|
img_array = []
|
||||||
|
for img_elem in img_list:
|
||||||
|
img_array.append({"url": img_elem.get_attribute("src")}) #, "bytes": HttpReq(img_url).read()})
|
||||||
|
SendMessage(context, {
|
||||||
|
"TextPlain": f'"{prompt}"',
|
||||||
|
"TextMarkdown": (f'"_{CharEscape(prompt, "MARKDOWN")}_"'),
|
||||||
|
"media": img_array,
|
||||||
|
})
|
||||||
|
return closeSelenium(driver_index, driver)
|
||||||
|
raise Exception("VM timed out.")
|
||||||
|
except Exception as error:
|
||||||
|
Log(format_exc())
|
||||||
|
SendMessage(context, {"TextPlain": "An unexpected error occurred."})
|
||||||
|
closeSelenium(driver_index, driver)
|
||||||
|
|
||||||
RegisterModule(name="Scrapers", endpoints={
|
RegisterModule(name="Scrapers", endpoints={
|
||||||
"DALL-E": CreateEndpoint(["dalle"], summary="Sends an AI-generated picture from DALL-E 3 via Microsoft Bing.", handler=cDalleSelenium),
|
"DALL-E": CreateEndpoint(["dalle"], summary="Sends an AI-generated picture from DALL-E 3 via Microsoft Bing.", handler=cDalleSelenium),
|
||||||
|
"Craiyon": CreateEndpoint(["craiyon"], summary="Sends an AI-generated picture from Craiyon.com.", handler=cCraiyonSelenium),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -23,10 +23,11 @@ def luaAttributeFilter(obj, attr_name, is_setting):
|
|||||||
raise AttributeError("Access Denied.")
|
raise AttributeError("Access Denied.")
|
||||||
|
|
||||||
# TODO make print behave the same as normal Lua, and expose a function for printing without newlines
|
# TODO make print behave the same as normal Lua, and expose a function for printing without newlines
|
||||||
def cLua(context, data=None) -> None:
|
def cLua(context:EventContext, data:InputMessageData) -> None:
|
||||||
scriptText = (data.Body or (data.Quoted and data.Quoted.Body))
|
# TODO update quoted api getting
|
||||||
|
scriptText = (data.command.body or (data.Quoted and data.Quoted.Body))
|
||||||
if not scriptText:
|
if not scriptText:
|
||||||
return SendMsg(context, {"Text": "You must provide some Lua code to execute."})
|
return SendMessage(context, {"Text": "You must provide some Lua code to execute."})
|
||||||
luaRuntime = NewLuaRuntime(max_memory=LuaMemoryLimit, register_eval=False, register_builtins=False, attribute_filter=luaAttributeFilter)
|
luaRuntime = NewLuaRuntime(max_memory=LuaMemoryLimit, register_eval=False, register_builtins=False, attribute_filter=luaAttributeFilter)
|
||||||
luaRuntime.eval(f"""(function()
|
luaRuntime.eval(f"""(function()
|
||||||
_windog = {{ stdout = "" }}
|
_windog = {{ stdout = "" }}
|
||||||
@ -49,7 +50,7 @@ return _windog.stdout .. (_windog.scriptout or '')
|
|||||||
end)()"""))
|
end)()"""))
|
||||||
except (LuaError, LuaSyntaxError) as error:
|
except (LuaError, LuaSyntaxError) as error:
|
||||||
Log(textOutput := ("Lua Error: " + str(error)))
|
Log(textOutput := ("Lua Error: " + str(error)))
|
||||||
SendMsg(context, {"TextPlain": textOutput})
|
SendMessage(context, {"TextPlain": textOutput})
|
||||||
|
|
||||||
RegisterModule(name="Scripting", group="Geek", summary="Tools for programming the bot and expanding its features.", endpoints={
|
RegisterModule(name="Scripting", group="Geek", summary="Tools for programming the bot and expanding its features.", endpoints={
|
||||||
"Lua": CreateEndpoint(["lua"], summary="Execute a Lua snippet and get its output.", handler=cLua),
|
"Lua": CreateEndpoint(["lua"], summary="Execute a Lua snippet and get its output.", handler=cLua),
|
||||||
|
93
WinDog.py
93
WinDog.py
@ -22,6 +22,22 @@ from LibWinDog.Database import *
|
|||||||
# <https://daringfireball.net/projects/markdown/syntax#backslash>
|
# <https://daringfireball.net/projects/markdown/syntax#backslash>
|
||||||
MdEscapes = '\\`*_{}[]()<>#+-.!|='
|
MdEscapes = '\\`*_{}[]()<>#+-.!|='
|
||||||
|
|
||||||
|
class SafeNamespace(SimpleNamespace):
|
||||||
|
def __getattribute__(self, value):
|
||||||
|
try:
|
||||||
|
return super().__getattribute__(value)
|
||||||
|
except AttributeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
class EventContext(SafeNamespace):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class InputMessageData(SafeNamespace):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class OutputMessageData(SafeNamespace):
|
||||||
|
pass
|
||||||
|
|
||||||
def Log(text:str, level:str="?", *, newline:bool|None=None, inline:bool=False) -> None:
|
def Log(text:str, level:str="?", *, newline:bool|None=None, inline:bool=False) -> None:
|
||||||
endline = '\n'
|
endline = '\n'
|
||||||
if newline == False or (inline and newline == None):
|
if newline == False or (inline and newline == None):
|
||||||
@ -114,13 +130,13 @@ def HtmlEscapeFull(Raw:str) -> str:
|
|||||||
def GetRawTokens(text:str) -> list:
|
def GetRawTokens(text:str) -> list:
|
||||||
return text.strip().replace('\t', ' ').replace(' ', ' ').replace(' ', ' ').split(' ')
|
return text.strip().replace('\t', ' ').replace(' ', ' ').replace(' ', ' ').split(' ')
|
||||||
|
|
||||||
def ParseCmd(msg) -> SimpleNamespace|None:
|
def ParseCmd(msg) -> SafeNamespace|None:
|
||||||
#if not len(msg) or msg[1] not in CmdPrefixes:
|
#if not len(msg) or msg[1] not in CmdPrefixes:
|
||||||
# return
|
# return
|
||||||
name = msg.replace('\n', ' ').replace('\t', ' ').replace(' ', ' ').replace(' ', ' ').split(' ')[0][1:].split('@')[0]
|
name = msg.replace('\n', ' ').replace('\t', ' ').replace(' ', ' ').replace(' ', ' ').split(' ')[0][1:].split('@')[0]
|
||||||
#if not name:
|
#if not name:
|
||||||
# return
|
# return
|
||||||
return SimpleNamespace(**{
|
return SafeNamespace(**{
|
||||||
"Name": name.lower(),
|
"Name": name.lower(),
|
||||||
"Body": name.join(msg.split(name)[1:]).strip(),
|
"Body": name.join(msg.split(name)[1:]).strip(),
|
||||||
"Tokens": GetRawTokens(msg),
|
"Tokens": GetRawTokens(msg),
|
||||||
@ -143,42 +159,46 @@ def RandHexStr(length:int) -> str:
|
|||||||
hexa += choice('0123456789abcdef')
|
hexa += choice('0123456789abcdef')
|
||||||
return hexa
|
return hexa
|
||||||
|
|
||||||
def ParseCommand(text:str) -> SimpleNamespace|None:
|
def ParseCommand(text:str) -> SafeNamespace|None:
|
||||||
|
command = SafeNamespace()
|
||||||
|
if not text:
|
||||||
|
return command
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
try: # ensure command is not empty
|
try: # ensure text is a non-empty command
|
||||||
if not (text[0] in CmdPrefixes and text[1:].strip()):
|
if not (text[0] in CmdPrefixes and text[1:].strip()):
|
||||||
return
|
return command
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return
|
return
|
||||||
command = SimpleNamespace(**{})
|
|
||||||
command.tokens = text.replace("\r", " ").replace("\n", " ").replace("\t", " ").replace(" ", " ").replace(" ", " ").split(" ")
|
command.tokens = text.replace("\r", " ").replace("\n", " ").replace("\t", " ").replace(" ", " ").replace(" ", " ").split(" ")
|
||||||
command.name = command.tokens[0][1:].lower()
|
command.name = command.tokens[0][1:].lower()
|
||||||
command.body = text[len(command.tokens[0]):].strip()
|
command.body = text[len(command.tokens[0]):].strip()
|
||||||
|
if command.name not in Endpoints:
|
||||||
|
return command
|
||||||
if (endpoint_arguments := Endpoints[command.name]["arguments"]):
|
if (endpoint_arguments := Endpoints[command.name]["arguments"]):
|
||||||
command.arguments = {}
|
command.arguments = {}
|
||||||
# TODO differences between required (True) and optional (False) args
|
index = 1
|
||||||
for index, key in enumerate(endpoint_arguments):
|
for key in endpoint_arguments:
|
||||||
|
if not endpoint_arguments[key]:
|
||||||
|
continue # skip optional (False) arguments for now, they will be implemented later
|
||||||
try:
|
try:
|
||||||
value = command.tokens[index + 1]
|
value = command.tokens[index]
|
||||||
command.body = command.body[len(value):].strip()
|
command.body = command.body[len(value):].strip()
|
||||||
except IndexError:
|
except IndexError:
|
||||||
value = None
|
value = None
|
||||||
command.arguments[key] = value
|
command.arguments[key] = value
|
||||||
|
index += 1
|
||||||
return command
|
return command
|
||||||
|
|
||||||
def OnMessageParsed(data:SimpleNamespace) -> None:
|
def OnMessageParsed(data:InputMessageData) -> None:
|
||||||
if Debug and (DumpToFile or DumpToConsole):
|
if Debug and (DumpToFile or DumpToConsole):
|
||||||
text = (data.text_auto.replace('\n', '\\n') if data.text_auto else '')
|
text = (data.text_auto.replace('\n', '\\n') if data.text_auto else '')
|
||||||
text = f"[{int(time.time())}] [{time.ctime()}] [{data.room_id}] [{data.message_id}] [{data.user.id}] {text}"
|
text = f"[{int(time.time())}] [{time.ctime()}] [{data.room.id}] [{data.message_id}] [{data.user.id}] {text}"
|
||||||
if DumpToConsole:
|
if DumpToConsole:
|
||||||
print(text)
|
print(text)
|
||||||
if DumpToFile:
|
if DumpToFile:
|
||||||
open((DumpToFile if (DumpToFile and type(DumpToFile) == str) else "./Dump.txt"), 'a').write(text + '\n')
|
open((DumpToFile if (DumpToFile and type(DumpToFile) == str) else "./Dump.txt"), 'a').write(text + '\n')
|
||||||
|
|
||||||
def SendMsg(context, data, destination=None) -> None:
|
def SendMessage(context, data:OutputMessageData, destination=None) -> None:
|
||||||
return SendMessage(context, data, destination)
|
|
||||||
|
|
||||||
def SendMessage(context, data, destination=None) -> None:
|
|
||||||
if type(context) == dict:
|
if type(context) == dict:
|
||||||
event = context['Event'] if 'Event' in context else None
|
event = context['Event'] if 'Event' in context else None
|
||||||
manager = context['Manager'] if 'Manager' in context else None
|
manager = context['Manager'] if 'Manager' in context else None
|
||||||
@ -195,14 +215,14 @@ def SendMessage(context, data, destination=None) -> None:
|
|||||||
textMarkdown = CharEscape(HtmlUnescape(data['Text']), InferMdEscape(HtmlUnescape(data['Text']), textPlain))
|
textMarkdown = CharEscape(HtmlUnescape(data['Text']), InferMdEscape(HtmlUnescape(data['Text']), textPlain))
|
||||||
for platform in Platforms:
|
for platform in Platforms:
|
||||||
platform = Platforms[platform]
|
platform = Platforms[platform]
|
||||||
if isinstanceSafe(event, InDict(platform, "eventClass")) or isinstanceSafe(manager, InDict(platform, "managerClass")):
|
if isinstanceSafe(event, platform.eventClass) or isinstanceSafe(manager, platform.managerClass):
|
||||||
platform["sender"](event, manager, data, destination, textPlain, textMarkdown)
|
platform.sender(event, manager, data, destination, textPlain, textMarkdown)
|
||||||
|
|
||||||
def SendReaction() -> None:
|
def SendNotice(context, data) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def RegisterPlatform(name:str, main:callable, sender:callable, *, eventClass=None, managerClass=None) -> None:
|
def RegisterPlatform(name:str, main:callable, sender:callable, linker:callable=None, *, eventClass=None, managerClass=None) -> None:
|
||||||
Platforms[name] = {"main": main, "sender": sender, "eventClass": eventClass, "managerClass": managerClass}
|
Platforms[name] = SafeNamespace(main=main, sender=sender, linker=linker, eventClass=eventClass, managerClass=managerClass)
|
||||||
Log(f"{name}, ", inline=True)
|
Log(f"{name}, ", inline=True)
|
||||||
|
|
||||||
def RegisterModule(name:str, endpoints:dict, *, group:str|None=None, summary:str|None=None) -> None:
|
def RegisterModule(name:str, endpoints:dict, *, group:str|None=None, summary:str|None=None) -> None:
|
||||||
@ -217,12 +237,28 @@ def RegisterModule(name:str, endpoints:dict, *, group:str|None=None, summary:str
|
|||||||
def CreateEndpoint(names:list[str], handler:callable, arguments:dict[str, bool]|None=None, *, summary:str|None=None) -> dict:
|
def CreateEndpoint(names:list[str], handler:callable, arguments:dict[str, bool]|None=None, *, summary:str|None=None) -> dict:
|
||||||
return {"names": names, "summary": summary, "handler": handler, "arguments": arguments}
|
return {"names": names, "summary": summary, "handler": handler, "arguments": arguments}
|
||||||
|
|
||||||
|
def WriteNewConfig() -> None:
|
||||||
|
Log("💾️ No configuration found! Generating and writing to `./Config.py`... ", inline=True)
|
||||||
|
with open("./Config.py", 'w') as configFile:
|
||||||
|
opening = '# windog config start #'
|
||||||
|
closing = '# end windog config #'
|
||||||
|
for folder in ("LibWinDog", "ModWinDog"):
|
||||||
|
for file in glob(f"./{folder}/**/*.py", recursive=True):
|
||||||
|
try:
|
||||||
|
name = '/'.join(file.split('/')[1:-1])
|
||||||
|
heading = f"# ==={'=' * len(name)}=== #"
|
||||||
|
source = open(file, 'r').read().replace(f"''' {opening}", f'""" {opening}').replace(f"{closing} '''", f'{closing} """')
|
||||||
|
content = '\n'.join(content.split(f'""" {opening}')[1].split(f'{closing} """')[0].split('\n')[1:-1])
|
||||||
|
configFile.write(f"{heading}\n# 🔽️ {name} 🔽️ #\n{heading}\n{content}\n\n")
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
def Main() -> None:
|
def Main() -> None:
|
||||||
#SetupDb()
|
#SetupDb()
|
||||||
SetupLocales()
|
SetupLocales()
|
||||||
Log(f"📨️ Initializing Platforms... ", newline=False)
|
Log(f"📨️ Initializing Platforms... ", newline=False)
|
||||||
for platform in Platforms:
|
for platform in Platforms:
|
||||||
if Platforms[platform]["main"]():
|
if Platforms[platform].main():
|
||||||
Log(f"{platform}, ", inline=True)
|
Log(f"{platform}, ", inline=True)
|
||||||
Log("...Done. ✅️", inline=True, newline=True)
|
Log("...Done. ✅️", inline=True, newline=True)
|
||||||
Log("🐶️ WinDog Ready!")
|
Log("🐶️ WinDog Ready!")
|
||||||
@ -265,20 +301,7 @@ if __name__ == '__main__':
|
|||||||
if isfile("./Config.py"):
|
if isfile("./Config.py"):
|
||||||
from Config import *
|
from Config import *
|
||||||
else:
|
else:
|
||||||
Log("💾️ No configuration found! Generating and writing to `./Config.py`... ", inline=True)
|
WriteNewConfig()
|
||||||
with open("./Config.py", 'w') as configFile:
|
|
||||||
opening = '# windog config start #'
|
|
||||||
closing = '# end windog config #'
|
|
||||||
for folder in ("LibWinDog", "ModWinDog"):
|
|
||||||
for file in glob(f"./{folder}/**/*.py", recursive=True):
|
|
||||||
try:
|
|
||||||
name = '/'.join(file.split('/')[1:-1])
|
|
||||||
heading = f"# ==={'=' * len(name)}=== #"
|
|
||||||
source = open(file, 'r').read().replace(f"''' {opening}", f'""" {opening}').replace(f"{closing} '''", f'{closing} """')
|
|
||||||
content = '\n'.join(content.split(f'""" {opening}')[1].split(f'{closing} """')[0].split('\n')[1:-1])
|
|
||||||
configFile.write(f"{heading}\n# 🔽️ {name} 🔽️ #\n{heading}\n{content}\n\n")
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
Log("Done. ✅️", inline=True, newline=True)
|
Log("Done. ✅️", inline=True, newline=True)
|
||||||
|
|
||||||
Main()
|
Main()
|
||||||
|
Reference in New Issue
Block a user