This commit is contained in:
Soumt Nam 2023-09-08 09:49:37 +09:00
commit 6835bf8fb0
10 changed files with 693 additions and 0 deletions

180
.gitignore vendored Normal file
View file

@ -0,0 +1,180 @@
# Created by https://www.toptal.com/developers/gitignore/api/python,dotenv
# Edit at https://www.toptal.com/developers/gitignore?templates=python,dotenv
### dotenv ###
.env
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
.vim/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# ruff
.ruff_cache/
# LSP config files
pyrightconfig.json
# End of https://www.toptal.com/developers/gitignore/api/python,dotenv

12
DummyModule.py Normal file
View file

@ -0,0 +1,12 @@
from module_interface import ModuleInterface
class MyModule(ModuleInterface):
def __init__(self):
super().__init__()
self.name = "DummyModule1"
self.regex_pattern = None #r"my_pattern"
#print("[Dummy1] DummyModule1 loaded.")
def execute_module(self, ctx):
#print(f"[Dummy1] {self.name} 실행: {ctx}")
pass

2
example.env Normal file
View file

@ -0,0 +1,2 @@
BOT_INSTANCE=(Your Misskey Instance)
BOT_KEY=(Your Misskey api key)

90
minya.py Normal file
View file

@ -0,0 +1,90 @@
from pyfiglet import Figlet
f = Figlet(font='small')
import os
import importlib.util
import re
import inspect
from dotenv import load_dotenv
from module_interface import ModuleInterface, ModuleManager
import asyncio
from aiohttp import ClientWebSocketResponse
from mipac.models.notification import NotificationNote
from mipa.ext.commands.bot import Bot
def load_modules():
module_dir = "modules"
modules = []
for filename in os.listdir(module_dir):
if filename.endswith(".py"):
module_name = os.path.splitext(filename)[0]
module_path = os.path.join(module_dir, filename)
spec = importlib.util.spec_from_file_location(module_name, module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
for attribute_name in dir(module):
attribute = getattr(module, attribute_name)
if inspect.isclass(attribute) and issubclass(attribute, ModuleInterface) and attribute != ModuleInterface:
modules.append(attribute())
return modules
class MiNya(Bot):
def __init__(self):
super().__init__()
self.__modules = []
self.__manager = ModuleManager(self)
async def _connect_channel(self):
await self.router.connect_channel(['main'])
async def on_reconnect(self, ws: ClientWebSocketResponse):
await self._connect_channel()
async def on_ready(self, ws: ClientWebSocketResponse):
await self._connect_channel()
print("[Main] Bot System Ready.")
print("")
print("[Main] Loading Modules...")
print("")
self.__modules = load_modules()
print("")
print(f"[Main] Loaded Modules : {len(self.__modules)}")
print("")
print("[Main] Registering Modules...")
for module in self.__modules:
self.__manager.register_module(module)
print("")
async def on_mention(self, notice: NotificationNote):
print("[Main] Got Mentioned!")
for _, module in self.__manager.modules.items():
if module.regex_pattern and re.match(module.regex_pattern, notice.note.content):
print("[Main] Regex Matching Module found : ", module.name)
if await module.execute_module(notice.note):
break
def main():
print(f.renderText('MiNya V1'))
print('"MIsskey Networked Yet Ambitious"')
print('')
print("[Main] Minya V1 Bootstap...")
print("[Main] Loading settings...")
load_dotenv()
instance = os.environ.get('BOT_INSTANCE')
key = os.environ.get('BOT_KEY')
if not (instance and key):
print("[MAIN] ERR! please check .env or os environment!")
return
bot = MiNya()
asyncio.run(bot.start(f'wss://{instance}/streaming', key))
if __name__ == "__main__":
main()

38
module_interface.py Normal file
View file

@ -0,0 +1,38 @@
from mipa.ext.commands.bot import Bot
class ModuleInterface:
def __init__(self):
self.name = None
self.regex_pattern = None
self.funcs = {}
self.manager: (ModuleManager | None) = None
def set_manager(self, manager):
self.manager = manager
self.module_ready()
def get_execute_pattern(self, regex_pattern):
return self.__regex_pattern
def module_ready(self):
pass
async def execute_module(self, ctx):
pass
def get_func(self, name):
return self.funcs[name]
class ModuleManager:
def __init__(self, main_bot = None):
self.modules = {}
self.bot: (Bot | None) = main_bot
def require(self, name):
return self.modules[name]
def register_module(self, module):
if module.name not in self.modules:
self.modules[module.name] = module
module.set_manager(self)

247
modules/Ai_tem.py Normal file
View file

@ -0,0 +1,247 @@
from module_interface import ModuleInterface
import random
itemPrefixes = [
'플라티나로 만든',
'신선한',
'최신의',
'고대의',
'수제',
'시계장치의',
'전설의',
'구이',
'날것의',
'미냐가 만든',
'포켓 사이즈',
'사흘 전의',
'그 근처의',
'',
'사용된',
'고장난',
'시판되는',
'주문제작된',
'업무용의',
'Microsoft제',
'Apple제',
'인류 기술의 결정체인',
'2021년산',
'500kg정도의',
'고오급',
'썩은',
'인공지능 탑재',
'블록체인 탑재',
'반중력',
'접이식',
'휴대용',
'유전자 재조합',
'돌연변이로 비행 능력이 있는',
'순금으로 만든',
'투명한',
'빛나는',
'하트 모양의',
'움직이는',
'반으로 잘린',
'USB 커넥터가 달린',
'지난 날의',
'저주받은',
'인챈트된',
'하루치의 비타민이 들어간',
'손을 댄',
'환상의',
'가상의',
'원자력',
'고도로 훈련받은',
'유전자 조작이 아닌',
'런던 중심부에서 발견된',
'이세계의',
'다른 별의',
'수수께끼의',
'시공을 일그러뜨리는',
'이상한 소리가 나는',
'무산된',
'플라즈마화된',
'충격을 주면 낮은 확률로 폭발하는',
'주키니로 변신한',
'가설의',
'독이 있는',
'진짜',
'궁극의',
'초코가 들어간',
'악취가 나는',
'4차원',
'박동하는',
'정체를 알 수 없는',
'네모난',
'날뛰는',
'꿈의',
'어둠의',
'암흑의',
'봉인된',
'죽음의',
'얼어버린',
'마의',
'금단의',
'홀로그래픽',
'유압식',
]
items = [
'가지',
'토마토',
'오이',
'감자',
'볶음국수',
'허리',
'초밥',
'호박',
'유키치',
'금괴',
'알루미늄',
'나트륨',
'마그네슘',
'플루토늄',
'작은 금속',
'우유팩',
'페트병',
'쿠키',
'초콜릿',
'메이드복',
'오렌지',
'니삭스',
'반물질 콘덴서',
'입자가속기',
'마이크로프로세서 (8코어 16스레드)',
'원자력 발전소',
'L4 스위치',
'완충 체인',
'양전자 두뇌',
'행성',
'테르민',
'충치차',
'마운터',
'버킷 휠 익스커베이터',
'데몬 코어',
'게임보이 어드밴스',
'양자컴퓨터',
'아나몰픽 렌즈',
'벽장에서 나온 수수께끼의 생물',
'스마트폰',
'시계',
'푸딩',
'가브리엘의 나팔',
'맹거의 스펀지',
'피젯 스피너',
'초입방체',
'건축물',
'에너지 드링크',
'마우스 커서',
'안경',
'참치',
'쓰레기통',
'이쑤시개',
'도시락에 들어가는 초록색 칸막이같은 녀석',
'나무젓가락',
'환기구',
'페트병의 뚜껑',
'소파 블럭',
'피자',
'치약',
'깡통',
'열쇠고리',
'금발 벽안의 미소녀',
'SD카드',
'립 크림',
'초코 없는 초코소라빵',
'조류독감',
'자판기',
'무거운 것',
'노트북',
'육포',
'연어 치즈',
'다이아몬드',
'물체',
'월석',
'특이점',
'중성자별',
'액체',
'위성',
'주키니',
'검은 것',
'흰 것',
'빨간 것',
'동그란 것',
'네모난 것',
'카드 모양의 것',
'기체',
'연필',
'지우개',
'양날검',
'막대 모양의 것',
'농산물',
'메탈 슬라임',
'문어발',
'버섯',
'나메코',
'호로요이',
'손톱깎기',
'귓속말',
'인형',
'티라노사우르스',
'요로결석',
'엔터 키',
'항아리',
'수은',
'DHMO',
'',
'토지',
'대륙',
'주사위',
'실외기',
'유압잭',
'타피오카',
'PSP',
'화장지 심지',
'골판지 상자',
'하니와',
'볼펜',
'샤펜',
'원자',
'우주',
'소립자',
'참기름',
'undefined',
'None',
'NAN',
'[object Object]'
]
ands = [
'에 빙의한',
'에 들어가는',
'에 묻힌',
'을 연상시키는',
' 같은',
'로 가장한',
'에 올려진',
' 옆에 있는',
];
from mipac.models import Note
class AiTem(ModuleInterface):
def __init__(self):
super().__init__()
self.name = "Ai-tem"
self.regex_pattern = ".*Test_AI-tem"
print("[Ai-tem] Ai-like item generator, Ai-tem V1 loaded.")
async def execute_module(self, ctx: Note):
print("[Ai-tem] test Generate Note")
await ctx.api.action.reply(str(self._generate()), visibility="home")
def _generate(self):
return random.choice(itemPrefixes) + ' ' + random.choice(items) + ((random.choice(ands) + ' ' + random.choice(itemPrefixes) + ' ' + random.choice(items)) if random.choice([True, False]) else "")
def module_ready(self):
self.funcs["generate"] = self._generate
print("[Ai-tem] Module Ready.")

53
modules/Amumal.py Normal file
View file

@ -0,0 +1,53 @@
from module_interface import ModuleInterface
from mipa.ext import tasks
import random
Amumals = {
"notes": [
"아무말 모듈 테스트 중이에요!",
"별거 아닌 일에는 '어?' 금지에요!",
"미레라도의 아이돌, 미냐에요!",
"빙구르르...",
"잠이여 물러가세요!",
"좋아... 이 버튼을 누르면..! 어라..?",
"겍...",
"리아님이 안놀아줘요...",
"미레라도? 미래라도? 헷갈려요!",
"배가 고파서 저기 메모리에 굴러다니던 푸딩을 먹었어요!",
"어라라, 분명 뭔가 하려고 했었는데..??",
#"가끔씩은 이웃집에도 놀러가보고 싶어요♪",
"피곤하신가요? 오늘도 수고하셨어요!",
"이불 밖은 던전이에요!",
"미냐 안자요",
"옆집에는 좋은 이름을 가진 친구가 있는 것 같아요!",
"아이와 친구가 되고 싶어요... 일본어는 못하지만요...",
"코...",
"리아님 놀아주세요!",
"저는 심연은 아니지만 여러분을 들여다 볼 수 있어요.",
"꾸벅...",
"레라님 일하세요!",
"집에 가고싶나요? 저도 가고싶어요!"
]
}
class Amumal(ModuleInterface):
def __init__(self):
super().__init__()
self.name = "Amumal"
self.regex_pattern = r"//"
print("[Amumal] Random message module, Amumal V1 loaded.")
def module_ready(self):
print("[Amumal] Module Ready.")
self.amumal_task.start()
@tasks.loop(seconds=600.0)
async def amumal_task(self):
aitem = str(self.manager.require("Ai-tem").get_func("generate")())
if random.choice([True, False]):
await self.manager.bot.client.note.action.send(random.choice(Amumals["notes"] + [aitem + "이 가지고 싶어요...", "오늘 산책 중에 " + aitem + "을 봤어요!"]), visibility="home")
print("[Amumal] Sent Amumal!")

20
modules/Birthday.py Normal file
View file

@ -0,0 +1,20 @@
from module_interface import ModuleInterface
from mipa.ext import tasks
import random
class Birthday(ModuleInterface):
def __init__(self):
super().__init__()
self.name = "Birthday"
self.regex_pattern = r"//"
print("[Birthday] Happy birthday message module, Birthday V1 loaded.")
def module_ready(self):
print("[Birthday] Module Ready.")
self.birthday_task.start()
@tasks.loop(seconds=600.0)
async def birthday_task(self):
#awa
pass

20
modules/Follow.py Normal file
View file

@ -0,0 +1,20 @@
from module_interface import ModuleInterface
from mipac.models import Note
class Follow(ModuleInterface):
def __init__(self):
super().__init__()
self.name = "Follow"
self.regex_pattern = r".*팔로우\s*(부탁\s*해|해?\s*(주(\s*십\s*시\s*오|세요)|줘))"
print("[Follow] Auto follow module, Follow V1 loaded.")
async def execute_module(self, ctx: Note):
print(f"[Follow] Got Follow Related Message : {ctx.content}")
usr = await self.manager.bot.user.api.action.get(ctx.author.id)
if usr.is_following:
await ctx.api.action.reply("이미 팔로우 되어있어요..! 저를 잊으셨나요..?")
else:
await usr.api.follow.action.add()
await ctx.api.action.reply("잘 부탁드려요!")

31
modules/Timer.py Normal file
View file

@ -0,0 +1,31 @@
import re
import asyncio
from module_interface import ModuleInterface
class Timer(ModuleInterface):
def __init__(self):
super().__init__()
self.name = "Timer"
self.regex_pattern = r"((.|\n)*)(타이머\s.*(맞춰줘|설정해줘))|.*(뒤에\s*알려줘)"
print("[Timer] Settable timer module, Timer V1 loaded.")
async def execute_module(self, ctx):
print(f"[Timer] Got Timer Related Message : {ctx.content}")
text = ctx.content
seconds_query = re.search(r'([0-9]+)초', text)
minutes_query = re.search(r'([0-9]+)분', text)
hours_query = re.search(r'([0-9]+)(시간)', text)
seconds = int(seconds_query.group(1)) if seconds_query else 0
minutes = int(minutes_query.group(1)) if minutes_query else 0
hours = int(hours_query.group(1)) if hours_query else 0
if ((seconds + minutes + hours) == 0):
await ctx.api.action.reply("올바른 값이 아니에요...")
time = (seconds) + (60 * minutes) + (60 * 60 * hours)
await ctx.api.action.reply("타이머를 설정할게요!")
await asyncio.sleep(int(time))
await ctx.api.action.reply("시간이 다 되었어요!")