diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..3bfabfc --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,36 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Upload Python Package + +on: + release: + types: [published] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/README.md b/README.md index 7dfefe8..9dc5842 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,23 @@ # 👹 Samurai Telegram Bot -Simple, yet effective moderator bot for telegram. With reports, logs, profanity filter and more :3 + +Simple, yet effective moderator bot for telegram. With reports, logs, profanity filter and more :3 # Description + Personal bot, made for easy chat auto-moderation. -Adds reporting functionality, profanity filtering (both english & russian languages are supported), logging system via private channel and much more! -More of that, the bot code & functions can be easily extended and/or limited as you prefer. +Adds reporting functionality, profanity filtering (both english & russian languages are supported), logging system via +private channel and much more! +More of that, the bot code & functions can be easily extended and/or limited as you prefer. -*The code has NOT been polished and is provided "as is". There are a lot of code that are redundant and there are tons of improvements that can be made.* +*The code has NOT been polished and is provided "as is". There are a lot of code that are redundant and there are tons +of improvements that can be made.* # ToDo + https://trello.com/b/MbwAxjd1/xobot-official ## Credits + https://github.com/masteroncluster/py-censure - Profanity filter we used as a base https://github.com/MasterGroosha/telegram-report-bot - Reports system we used as a base diff --git a/announcements.py b/announcements.py index 66f21d3..ea54bbb 100644 --- a/announcements.py +++ b/announcements.py @@ -4,13 +4,15 @@ import asyncio import aioschedule as schedule + async def announce(message): - await dispatcher.dp.bot.send_message(configurator.config.groups.main, message) + await dispatcher.dp.bot.send_message(configurator.config.groups.main, message) + async def scheduler(): for i in localization.get_string('announcements'): - schedule.every(i['every']).seconds.do(announce, i['message']) + schedule.every(i['every']).seconds.do(announce, i['message']) while True: await schedule.run_pending() - await asyncio.sleep(2) \ No newline at end of file + await asyncio.sleep(2) diff --git a/bot.py b/bot.py index 7339d81..bb8d93b 100644 --- a/bot.py +++ b/bot.py @@ -5,6 +5,6 @@ import asyncio if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.create_task(announcements.scheduler()) - executor.start_polling(dp, skip_updates=True) \ No newline at end of file + loop = asyncio.get_event_loop() + loop.create_task(announcements.scheduler()) + executor.start_polling(dp, skip_updates=True) diff --git a/censure/censure/__init__.py b/censure/censure/__init__.py index 9cabc9f..9923ce8 100644 --- a/censure/censure/__init__.py +++ b/censure/censure/__init__.py @@ -1,4 +1,4 @@ from .base import Censor from .helper import CensorHelper -__all__ = ['Censor', 'CensorHelper'] \ No newline at end of file +__all__ = ['Censor', 'CensorHelper'] diff --git a/censure/censure/base.py b/censure/censure/base.py index ab47142..3de6232 100644 --- a/censure/censure/base.py +++ b/censure/censure/base.py @@ -87,7 +87,7 @@ def __init__(self, value=None, token_type=None): head = head[0].lower()[1:] if not token_type: - token_type = 'to' # open type ie + token_type = 'to' # open type ie # should derive from value if head[0] == '/': head = head[1:] diff --git a/censure/censure/lang/en/__init__.py b/censure/censure/lang/en/__init__.py index 630de5c..8679e5a 100644 --- a/censure/censure/lang/en/__init__.py +++ b/censure/censure/lang/en/__init__.py @@ -1,5 +1,4 @@ from . import constants from . import patterns - __all__ = ['constants', 'patterns'] diff --git a/censure/censure/lang/en/constants.py b/censure/censure/lang/en/constants.py index 868d4b5..b63fc25 100644 --- a/censure/censure/lang/en/constants.py +++ b/censure/censure/lang/en/constants.py @@ -3,12 +3,10 @@ from __future__ import unicode_literals - EXCLUDES_DATA = { } - EXCLUDES_CORE = { } @@ -127,7 +125,6 @@ 'bullshit': '^bullshit$', 'butt': '^butt((plug)|(pirate)|($))', - 'clit': '^clit(($)|(or)|(face))', 'cum': '^cum(($)|(bubble)|(dumpster)|(guzzler)|(jockey)|(slut)|(tart))', 'cunni': '^cunni(($)|(e)|(lingus))', @@ -169,7 +166,6 @@ 'whore': '^whore(bag|face|$)', } - BAD_SEMI_PHRASES = ( 'suckmydick', 'sickmyduck', diff --git a/censure/censure/lang/en/patterns.py b/censure/censure/lang/en/patterns.py index e5ff0df..151fa4d 100644 --- a/censure/censure/lang/en/patterns.py +++ b/censure/censure/lang/en/patterns.py @@ -2,31 +2,29 @@ from __future__ import unicode_literals import re +PAT_EUML = re.compile('&[Ee][Uu][Mm][Ll];') # e +PAT_IUML = re.compile('&[Uu][Uu][Mm][Ll];') # и +PAT_AUML = re.compile('&[Aa][Uu][Mm][Ll];') # а +PAT_OUML = re.compile('&[Oo][Uu][Mm][Ll];') # о +PAT_YUML = re.compile('&[Yy][Uu][Mm][Ll];') # у -PAT_EUML = re.compile('&[Ee][Uu][Mm][Ll];') # e -PAT_IUML = re.compile('&[Uu][Uu][Mm][Ll];') # и -PAT_AUML = re.compile('&[Aa][Uu][Mm][Ll];') # а -PAT_OUML = re.compile('&[Oo][Uu][Mm][Ll];') # о -PAT_YUML = re.compile('&[Yy][Uu][Mm][Ll];') # у - -PAT_CENT = re.compile('&[Cc][Ee][Nn][Tt];') # c -PAT_CODE203 = re.compile('Ë') # e -PAT_CODE162 = re.compile('¢') # c -PAT_CODE120 = re.compile('x') # х -PAT_CODE121 = re.compile('y') # у - +PAT_CENT = re.compile('&[Cc][Ee][Nn][Tt];') # c +PAT_CODE203 = re.compile('Ë') # e +PAT_CODE162 = re.compile('¢') # c +PAT_CODE120 = re.compile('x') # х +PAT_CODE121 = re.compile('y') # у PATTERNS_REPLACEMENTS = ( - (PAT_EUML, 'e'), # euml - (PAT_IUML, 'u'), # iuml - (PAT_AUML, 'a'), # auml - (PAT_OUML, 'o'), # ouml - (PAT_YUML, 'y'), # yuml + (PAT_EUML, 'e'), # euml + (PAT_IUML, 'u'), # iuml + (PAT_AUML, 'a'), # auml + (PAT_OUML, 'o'), # ouml + (PAT_YUML, 'y'), # yuml (PAT_CODE120, 'x'), (PAT_CODE121, 'y'), (PAT_CODE203, 'e'), - (PAT_CENT, 'c'), # cent + (PAT_CENT, 'c'), # cent (PAT_CODE162, 'c'), ) diff --git a/censure/censure/lang/ru/__init__.py b/censure/censure/lang/ru/__init__.py index 630de5c..8679e5a 100644 --- a/censure/censure/lang/ru/__init__.py +++ b/censure/censure/lang/ru/__init__.py @@ -1,5 +1,4 @@ from . import constants from . import patterns - __all__ = ['constants', 'patterns'] diff --git a/censure/censure/lang/ru/constants.py b/censure/censure/lang/ru/constants.py index 71d5add..cae0e2f 100644 --- a/censure/censure/lang/ru/constants.py +++ b/censure/censure/lang/ru/constants.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals - EXCLUDES_DATA = { 'а': [ 'автомоб', @@ -419,7 +418,6 @@ ] } - EXCLUDES_CORE = { 'боле': 'боле', 'гре#1': '^.{0,3}греб$', @@ -489,7 +487,6 @@ 'япуч': 'яп[п]?уч' } - FOUL_DATA = { 'а': [ '^абанамат', @@ -1016,7 +1013,6 @@ ] } - BAD_SEMI_PHRASES = ( 'анепош[е]?л[и]?бы[вт]ы', 'еханыйбабай', diff --git a/censure/censure/lang/ru/patterns.py b/censure/censure/lang/ru/patterns.py index 8cb787b..8d4a366 100644 --- a/censure/censure/lang/ru/patterns.py +++ b/censure/censure/lang/ru/patterns.py @@ -2,77 +2,74 @@ from __future__ import unicode_literals import re +PAT_EUML = re.compile('&[Ee][Uu][Mm][Ll];') # e +PAT_IUML = re.compile('&[Uu][Uu][Mm][Ll];') # и +PAT_AUML = re.compile('&[Aa][Uu][Mm][Ll];') # а +PAT_OUML = re.compile('&[Oo][Uu][Mm][Ll];') # о +PAT_YUML = re.compile('&[Yy][Uu][Mm][Ll];') # у -PAT_EUML = re.compile('&[Ee][Uu][Mm][Ll];') # e -PAT_IUML = re.compile('&[Uu][Uu][Mm][Ll];') # и -PAT_AUML = re.compile('&[Aa][Uu][Mm][Ll];') # а -PAT_OUML = re.compile('&[Oo][Uu][Mm][Ll];') # о -PAT_YUML = re.compile('&[Yy][Uu][Mm][Ll];') # у +PAT_CENT = re.compile('&[Cc][Ee][Nn][Tt];') # c +PAT_CODE203 = re.compile('Ë') # e +PAT_CODE162 = re.compile('¢') # c +PAT_CODE120 = re.compile('x') # х +PAT_CODE121 = re.compile('y') # у -PAT_CENT = re.compile('&[Cc][Ee][Nn][Tt];') # c -PAT_CODE203 = re.compile('Ë') # e -PAT_CODE162 = re.compile('¢') # c -PAT_CODE120 = re.compile('x') # х -PAT_CODE121 = re.compile('y') # у +PAT_AS_I = re.compile(r'\|\\\|') # И +PAT_AS_L = re.compile(r'/\\') # Л -PAT_AS_I = re.compile(r'\|\\\|') # И -PAT_AS_L = re.compile(r'/\\') # Л +PAT_AS_X1 = re.compile(r'><') # Х +PAT_AS_X2 = re.compile(r'><') # Х +PAT_AS_X3 = re.compile('\)\(') # Х +PAT_AS_X4 = re.compile('}{') # Х -PAT_AS_X1 = re.compile(r'><') # Х -PAT_AS_X2 = re.compile(r'><') # Х -PAT_AS_X3 = re.compile('\)\(') # Х -PAT_AS_X4 = re.compile('}{') # Х +PAT_AS_J1 = re.compile('>\|<') # Ж +PAT_AS_J2 = re.compile('}\|{') # Ж -PAT_AS_J1 = re.compile('>\|<') # Ж -PAT_AS_J2 = re.compile('}\|{') # Ж - -PAT_AS_Y1 = re.compile('`/') # Y -PAT_AS_Y2 = re.compile('\-/') # Y -PAT_AS_Y3 = re.compile('`\-/') # Y - -PAT_AS_YY1 = re.compile('b\|') # ы -PAT_AS_YY2 = re.compile('bI') # ы -PAT_AS_YY3 = re.compile('bl') # ы +PAT_AS_Y1 = re.compile('`/') # Y +PAT_AS_Y2 = re.compile('\-/') # Y +PAT_AS_Y3 = re.compile('`\-/') # Y +PAT_AS_YY1 = re.compile('b\|') # ы +PAT_AS_YY2 = re.compile('bI') # ы +PAT_AS_YY3 = re.compile('bl') # ы PAT_PI = re.compile('3[\.,]14[\d]*') PAT_E = re.compile('2[\.,]72[\d]*') PAT_PREP = re.compile('(а[х]?)|(в)|([вмт]ы)|(д[ао])|(же)|(за)') - PATTERNS_REPLACEMENTS = ( - (PAT_EUML, 'е'), # euml - (PAT_IUML, 'и'), # iuml - (PAT_AUML, 'а'), # auml - (PAT_OUML, 'о'), # ouml - (PAT_YUML, 'у'), # yuml + (PAT_EUML, 'е'), # euml + (PAT_IUML, 'и'), # iuml + (PAT_AUML, 'а'), # auml + (PAT_OUML, 'о'), # ouml + (PAT_YUML, 'у'), # yuml (PAT_CODE203, 'е'), - (PAT_CENT, 'с'), # cent + (PAT_CENT, 'с'), # cent (PAT_CODE162, 'с'), - (PAT_AS_I, 'и'), # as И - (PAT_AS_L, 'л'), # as Л + (PAT_AS_I, 'и'), # as И + (PAT_AS_L, 'л'), # as Л - (PAT_AS_X1, 'х'), # as Х - (PAT_AS_X2, 'х'), # as Х - (PAT_AS_X3, 'х'), # as Х - (PAT_AS_X4, 'х'), # as Х + (PAT_AS_X1, 'х'), # as Х + (PAT_AS_X2, 'х'), # as Х + (PAT_AS_X3, 'х'), # as Х + (PAT_AS_X4, 'х'), # as Х - (PAT_AS_J1, 'ж'), # as ж - (PAT_AS_J2, 'ж'), # as ж + (PAT_AS_J1, 'ж'), # as ж + (PAT_AS_J2, 'ж'), # as ж - (PAT_AS_Y1, 'y'), # as y - (PAT_AS_Y2, 'y'), # as y - (PAT_AS_Y3, 'y'), # as y + (PAT_AS_Y1, 'y'), # as y + (PAT_AS_Y2, 'y'), # as y + (PAT_AS_Y3, 'y'), # as y - (PAT_AS_YY1, 'ы'), # as ы - (PAT_AS_YY2, 'ы'), # as ы - (PAT_AS_YY3, 'ы'), # as ы + (PAT_AS_YY1, 'ы'), # as ы + (PAT_AS_YY2, 'ы'), # as ы + (PAT_AS_YY3, 'ы'), # as ы (PAT_CODE120, 'х'), (PAT_CODE121, 'у'), - (PAT_PI, 'пи'), # 3.14... - (PAT_E, 'е'), # 2.72... + (PAT_PI, 'пи'), # 3.14... + (PAT_E, 'е'), # 2.72... ) diff --git a/censure/censure/tests/base.py b/censure/censure/tests/base.py index 31f19ca..ce64ba4 100644 --- a/censure/censure/tests/base.py +++ b/censure/censure/tests/base.py @@ -9,6 +9,7 @@ from censure.base import Censor, CensorException from censure.lang.common import constants, patterns + # from censure.tests.data import ( # SIMPLE_OBSCENE_WORDS, E_OBSCENE_WORDS, PI_OBSCENE_WORDS, # OBSCENE_HTML_LINES, diff --git a/censure/censure/tests/ru/data.py b/censure/censure/tests/ru/data.py index a07b533..730f1ec 100644 --- a/censure/censure/tests/ru/data.py +++ b/censure/censure/tests/ru/data.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals from censure.lang.common.constants import BEEP - SIMPLE_OBSCENE_WORDS = ( 'пидорги', 'ебланаи', diff --git a/censure/censure/tests/ru/test_base.py b/censure/censure/tests/ru/test_base.py index d91bccc..61b6420 100644 --- a/censure/censure/tests/ru/test_base.py +++ b/censure/censure/tests/ru/test_base.py @@ -8,11 +8,11 @@ class CensorInternalsTestCase(TestCaseRu): def test__is_pi_or_e_word(self): for w, result in ( - ('2.72', True), - ('3.14', True), - ('2.71', False), - ('3.15', False), - ('5.15', False), + ('2.72', True), + ('3.14', True), + ('2.71', False), + ('3.15', False), + ('5.15', False), ): if self._dice(): # prefix w = '{}{}'.format(self._get_random_word(), w) diff --git a/config.ini b/config.ini index b14676d..4a26f54 100644 --- a/config.ini +++ b/config.ini @@ -1,12 +1,15 @@ [bot] -owner=; -token=; -language=ru -version=0.4 -version_codename=Solid samurai +owner =; +token =; +language = ru +version = 0.4 +version_codename = Solid samurai [groups] -main=0 -reports=0 -logs=0 -new_users_nomedia=7776000 \ No newline at end of file +main = 0 +reports = 0 +logs = 0 +new_users_nomedia = 7776000 + +[database] +url =; \ No newline at end of file diff --git a/configurator.py b/configurator.py index c4ebda4..cbce771 100644 --- a/configurator.py +++ b/configurator.py @@ -4,6 +4,7 @@ config = edict() + def make_config(filename): parser = ConfigParser() parser.read(filename) @@ -17,4 +18,4 @@ def make_config(filename): for key, value in parser.items(section): config[section][key] = value - return True \ No newline at end of file + return True diff --git a/database/__init__.py b/database/__init__.py new file mode 100644 index 0000000..9917a30 --- /dev/null +++ b/database/__init__.py @@ -0,0 +1 @@ +from .users import * diff --git a/database/connection.py b/database/connection.py new file mode 100644 index 0000000..fca4c28 --- /dev/null +++ b/database/connection.py @@ -0,0 +1,6 @@ +import asyncpg +from asyncpg import Pool +import configurator + +conn_poll: Pool = await asyncpg.create_pool(dsn=configurator.config.database.url) +conn = await conn_poll.acquire() diff --git a/database/queries.py b/database/queries.py new file mode 100644 index 0000000..2b7d1b4 --- /dev/null +++ b/database/queries.py @@ -0,0 +1,4 @@ +add_user = ''' +INSERT INTO users(telegram_id) +VALUES($1); +''' diff --git a/database/tables.sql b/database/tables.sql new file mode 100644 index 0000000..042a196 --- /dev/null +++ b/database/tables.sql @@ -0,0 +1,8 @@ +--DROP TABLE IF EXISTS users; + +CREATE TABLE users( + column_id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + telegram_id SMALLINT UNIQUE, + banned BOOLEAN DEFAULT FALSE, + join_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); diff --git a/database/users.py b/database/users.py new file mode 100644 index 0000000..44d1db6 --- /dev/null +++ b/database/users.py @@ -0,0 +1,6 @@ +from connection import conn +from queries import add_user + + +async def add(telg_id) -> None: + await conn.execute(add_user, telg_id) diff --git a/filters.py b/filters.py index 2abd018..77895c9 100644 --- a/filters.py +++ b/filters.py @@ -29,4 +29,4 @@ async def check(self, message: types.Message): member = await message.bot.get_chat_member(message.chat.id, message.from_user.id) # I don't know why, but telegram thinks, if member is chat creator, he cant restrict member - return (member.is_chat_creator() or member.can_restrict_members) == self.member_can_restrict \ No newline at end of file + return (member.is_chat_creator() or member.can_restrict_members) == self.member_can_restrict diff --git a/handlers/__init__.py b/handlers/__init__.py index 47ba69d..78bfb7c 100644 --- a/handlers/__init__.py +++ b/handlers/__init__.py @@ -4,4 +4,4 @@ from . import user_actions from . import callbacks from . import personal_actions -from . import group_events \ No newline at end of file +from . import group_events diff --git a/handlers/callbacks.py b/handlers/callbacks.py index 7649fc1..b68c019 100644 --- a/handlers/callbacks.py +++ b/handlers/callbacks.py @@ -8,6 +8,7 @@ from aiogram.utils.exceptions import (MessageToEditNotFound, MessageCantBeEdited, MessageCantBeDeleted, MessageToDeleteNotFound) + @dp.callback_query_handler() async def callback_handler(call: types.CallbackQuery): """ @@ -52,7 +53,7 @@ async def callback_handler(call: types.CallbackQuery): elif call.data.startswith("mute2_"): with suppress(MessageCantBeDeleted, MessageToDeleteNotFound): await call.message.bot.delete_message(config.groups.main, int(call.data.split("_")[1])) - + await call.message.bot.restrict_chat_member(chat_id=config.groups.main, user_id=call.data.split("_")[2], permissions=types.ChatPermissions(), until_date=int(time()) + ((3600 * 24) * 7)) # 7 days from now @@ -102,4 +103,4 @@ async def callback_handler(call: types.CallbackQuery): message_id=call.message.message_id, text=call.message.text + localization.get_string( "action_deleted_dismissed4")) - await call.answer(text="Done") \ No newline at end of file + await call.answer(text="Done") diff --git a/handlers/exceptions.py b/handlers/exceptions.py index 9379edc..3b5e632 100644 --- a/handlers/exceptions.py +++ b/handlers/exceptions.py @@ -6,6 +6,7 @@ from dispatcher import dp + @dp.errors_handler() async def errors_handler(update, exception): """ @@ -52,5 +53,5 @@ async def errors_handler(update, exception): if isinstance(exception, CantParseEntities): logging.exception(f'CantParseEntities: {exception} \nUpdate: {update}') return True - - logging.exception(f'Update: {update} \n{exception}') \ No newline at end of file + + logging.exception(f'Update: {update} \n{exception}') diff --git a/handlers/group_events.py b/handlers/group_events.py index 9fc4bd9..5aec3c9 100644 --- a/handlers/group_events.py +++ b/handlers/group_events.py @@ -1,4 +1,6 @@ from aiogram import types + +import database from configurator import config from dispatcher import dp import localization @@ -6,6 +8,7 @@ import re import utils + # blacklist = open("blacklist.txt", mode="r").read().split(',') # blacklist_regexp = re.compile(r'(?iu)\b((у|[нз]а|(хитро|не)?вз?[ыьъ]|с[ьъ]|(и|ра)[зс]ъ?|(о[тб]|под)[ьъ]?|(.\B)+?[оаеи])?-?([её]б(?!о[рй])|и[пб][ае][тц]).*?|(н[иеа]|[дп]о|ра[зс]|з?а|с(ме)?|о(т|дно)?|апч)?-?ху([яйиеёю]|ли(?!ган)).*?|(в[зы]|(три|два|четыре)жды|(н|сук)а)?-?бл(я(?!(х|ш[кн]|мб)[ауеыио]).*?|[еэ][дт]ь?)|(ра[сз]|[зн]а|[со]|вы?|п(р[ои]|од)|и[зс]ъ?|[ао]т)?п[иеё]зд.*?|(за)?п[ие]д[аое]?р((ас)?(и(ли)?[нщктл]ь?)?|(о(ч[еи])?)?к|юг)[ауеы]?|манд([ауеы]|ой|[ао]вошь?(е?к[ауе])?|юк(ов|[ауи])?)|муд([аио].*?|е?н([ьюия]|ей))|мля([тд]ь)?|лять|([нз]а|по)х|м[ао]л[ао]фь[яию])\b') @@ -26,35 +29,40 @@ async def on_user_join(message: types.Message): permissions=types.ChatPermissions(True), until_date=int(time()) + int(config.groups.new_users_nomedia))''' - await utils.write_log(message.bot, "Присоединился пользователь "+utils.user_mention(message.from_user), "Новый участник") + await database.add_user(message.from_user.id) + await utils.write_log(message.bot, "Присоединился пользователь " + utils.user_mention(message.from_user), + "Новый участник") + @dp.message_handler(is_admin=False, chat_id=config.groups.main) @dp.edited_message_handler(is_admin=False, chat_id=config.groups.main) async def on_user_message_censor_filter(message: types.Message): - """ - Removes messages, if they contain censored words. + """ + Removes messages, if they contain censored words. - :param message: Message in group - """ - _del = False - _word = None + :param message: Message in group + """ + _del = False + _word = None - _del, _word = utils.check_for_profanity_all(message.text) + _del, _word = utils.check_for_profanity_all(message.text) - # process - if _del: - await message.delete() + # process + if _del: + await message.delete() - log_msg = message.text - if _word: - log_msg = log_msg.replace(_word, ''+_word+'') - log_msg += "\n\nАвтор: "+utils.user_mention(message.from_user) + log_msg = message.text + if _word: + log_msg = log_msg.replace(_word, '' + _word + '') + log_msg += "\n\nАвтор: " + utils.user_mention(message.from_user) + + await utils.write_log(message.bot, log_msg, "Антимат") - await utils.write_log(message.bot, log_msg, "Антимат") @dp.message_handler(chat_id=config.groups.main, content_types=["voice"]) async def on_user_voice(message: types.Message): - await message.reply(localization.get_string("voice_message_reaction")) + await message.reply(localization.get_string("voice_message_reaction")) + '''async def on_user_message(message: types.Message): """ @@ -95,4 +103,4 @@ async def do_ro_filter_messages(message: types.Message): # but in this case there may be problems with restrictions (after restriction period ended, telegram returns # back the default chat restrictions, but not users previous restrictions) if builtins.RO and not user.is_chat_admin(): - await message.bot.delete_message(config.groups.main, message.message_id)''' \ No newline at end of file + await message.bot.delete_message(config.groups.main, message.message_id)''' diff --git a/handlers/personal_actions.py b/handlers/personal_actions.py index fca26d2..8fed0d8 100644 --- a/handlers/personal_actions.py +++ b/handlers/personal_actions.py @@ -7,79 +7,85 @@ import psutil import sys -sys.path.append("./censure") # allow module import from git submodule + +sys.path.append("./censure") # allow module import from git submodule from censure import Censor censor_ru = Censor.get(lang='ru') censor_en = Censor.get(lang='en') -@dp.message_handler(user_id = int(config.bot.owner), commands="msg", commands_prefix="!/") + +@dp.message_handler(user_id=int(config.bot.owner), commands="msg", commands_prefix="!/") async def cmd_message_from_bot(message: types.Message): - await message.bot.send_message(config.groups.main, utils.remove_prefix(message.text, "!msg ")) + await message.bot.send_message(config.groups.main, utils.remove_prefix(message.text, "!msg ")) -@dp.message_handler(user_id = int(config.bot.owner), commands="log", commands_prefix="!/") + +@dp.message_handler(user_id=int(config.bot.owner), commands="log", commands_prefix="!/") async def cmd_write_log_bot(message: types.Message): - await utils.write_log(message.bot, utils.remove_prefix(message.text, "!log "), "test") + await utils.write_log(message.bot, utils.remove_prefix(message.text, "!log "), "test") + @dp.message_handler(commands="ping", commands_prefix="!") async def cmd_ping_bot(message: types.Message): - # Check if command is sent by group admin - user = await message.bot.get_chat_member(config.groups.main, message.from_user.id) - if user.is_chat_admin(): + # Check if command is sent by group admin + user = await message.bot.get_chat_member(config.groups.main, message.from_user.id) + if user.is_chat_admin(): + ram = psutil.virtual_memory() - ram = psutil.virtual_memory() + reply = "👊 Up & Running!\n\n" + reply += "CPU: " + str(psutil.cpu_count()) + " cores (" + str( + psutil.cpu_freq().max) + "MHz) with " + str(psutil.cpu_percent()) + "% current usage\n" + reply += "RAM: " + str(ram.used >> 20) + "mb / " + str(ram.total >> 20) + "mb\n"; - reply = "👊 Up & Running!\n\n" - reply += "CPU: " + str(psutil.cpu_count()) + " cores (" + str(psutil.cpu_freq().max) + "MHz) with " + str(psutil.cpu_percent()) + "% current usage\n" - reply += "RAM: " + str(ram.used >> 20) +"mb / "+ str(ram.total >> 20) + "mb\n"; + reply += "\nBot version: " + str( + config.bot.version) + " codename «" + config.bot.version_codename + "» 🌚" - reply += "\nBot version: " + str(config.bot.version) + " codename «" + config.bot.version_codename + "» 🌚" + await message.reply(reply) - await message.reply(reply) @dp.message_handler(commands="prof", commands_prefix="!") async def cmd_profanity_check(message: types.Message): - # Check if command is sent by group admin - user = await message.bot.get_chat_member(config.groups.main, message.from_user.id) - if user.is_chat_admin(): - _del = False - _word = None - _pat = None - - line_info_ru = censor_ru.clean_line(utils.remove_prefix(message.text, "!prof ")) - line_info_en = censor_en.clean_line(utils.remove_prefix(message.text, "!prof ")) - - # line, bad_words_count, bad_phrases_count, detected_bad_words, detected_bad_phrases - - # check RU - if line_info_ru[1] or line_info_ru[2]: - if line_info_ru[1]: - _word = line_info_ru[3][0] - else: - _word = line_info_ru[4][0] - - _pat = line_info_ru[5][0] - _del = True - - # check ENG - if line_info_en[1] or line_info_en[2]: - if line_info_en[1]: - _word = line_info_en[3][0] - else: - _word = line_info_en[4][0] - - _pat = line_info_en[5][0] - _del = True - - # process - if _del: - log_msg = message.text - if _word: - log_msg = "❌ Profanity detected.\n\n" - log_msg += utils.remove_prefix(message.text, "!prof ").replace(_word, ''+_word+'') - log_msg += "\nПаттерн: " + _pat - - await message.reply(log_msg) - else: - await message.reply("✅ No profanity detected.") \ No newline at end of file + # Check if command is sent by group admin + user = await message.bot.get_chat_member(config.groups.main, message.from_user.id) + if user.is_chat_admin(): + _del = False + _word = None + _pat = None + + line_info_ru = censor_ru.clean_line(utils.remove_prefix(message.text, "!prof ")) + line_info_en = censor_en.clean_line(utils.remove_prefix(message.text, "!prof ")) + + # line, bad_words_count, bad_phrases_count, detected_bad_words, detected_bad_phrases + + # check RU + if line_info_ru[1] or line_info_ru[2]: + if line_info_ru[1]: + _word = line_info_ru[3][0] + else: + _word = line_info_ru[4][0] + + _pat = line_info_ru[5][0] + _del = True + + # check ENG + if line_info_en[1] or line_info_en[2]: + if line_info_en[1]: + _word = line_info_en[3][0] + else: + _word = line_info_en[4][0] + + _pat = line_info_en[5][0] + _del = True + + # process + if _del: + log_msg = message.text + if _word: + log_msg = "❌ Profanity detected.\n\n" + log_msg += utils.remove_prefix(message.text, "!prof ").replace(_word, '' + _word + '') + log_msg += "\nПаттерн: " + _pat + + await message.reply(log_msg) + else: + await message.reply("✅ No profanity detected.") diff --git a/handlers/user_actions.py b/handlers/user_actions.py index 4a11eb9..7c4eac4 100644 --- a/handlers/user_actions.py +++ b/handlers/user_actions.py @@ -7,6 +7,7 @@ import utils import random + @dp.message_handler(chat_id=config.groups.main, commands="report", commands_prefix="/!") async def cmd_report(message: types.Message): # Check if command is sent as reply to some message @@ -98,6 +99,7 @@ async def cmd_report(message: types.Message): reply_markup=action_keyboard) await message.reply(localization.get_string("report_delivered")) + @dp.message_handler(Text(startswith="@admin", ignore_case=True), chat_id=config.groups.main) async def calling_all_units(message: types.Message): """ diff --git a/heroku_config.py b/heroku_config.py index 77b33b3..5abc365 100644 --- a/heroku_config.py +++ b/heroku_config.py @@ -1,4 +1,5 @@ import os + is_heroku = os.environ.get('IS_HEROKU', None) if is_heroku: @@ -10,4 +11,6 @@ config.groups.main = int(os.environ.get('GROUPS_MAIN', None)) config.groups.reports = int(os.environ.get('GROUPS_REPORTS', None)) - config.groups.logs = int(os.environ.get('GROUPS_LOGS', None)) \ No newline at end of file + config.groups.logs = int(os.environ.get('GROUPS_LOGS', None)) + + config.database.url = os.environ.get('DATABASE_URL', None) diff --git a/localization.py b/localization.py index 6f72d58..212b0bc 100644 --- a/localization.py +++ b/localization.py @@ -60,7 +60,7 @@ "action_false_alarm_2": "❎ Нарушений нет (🙊 мат репортера на день)", "action_false_alarm_3": "❎ Нарушений нет (🙊 мат репортера на неделю)", "action_false_alarm_4": "❎ Нарушений нет (❌ бан репортера)", - + "action_deleted": "\n\n🗑 Удалено", "action_deleted_banned": "\n\n🗑❌ Удалено, юзер забанен", "action_deleted_readonly": "\n\n🗑🙊 Удалено, + выдан мут на день.", @@ -108,18 +108,18 @@ "greetings_words": ("привет", "хай", "ку", "здарова"), # Бот среагирует на короткие сообщения с этими словами - "announcements" : ( + "announcements": ( { - "message" : "🌀 Участники чата, не забывайте про команду !report благодаря которой Вы можете обратить внимание администрации на нарушителя в чате.\nСпам данной командой карается вечным баном.", - "every" : 18000 + "message": "🌀 Участники чата, не забывайте про команду !report благодаря которой Вы можете обратить внимание администрации на нарушителя в чате.\nСпам данной командой карается вечным баном.", + "every": 18000 }, { - "message" : "Общаемся на тему программирования и всего что с ним связано 👊\n\n🕒 Все стикеры и медиа временно запрещены\n🤬 Ненормативная лексика запрещена\n👽 Оффтоп наказывается мутами\n\nПриятного общения 🥰", - "every" : 10800 + "message": "Общаемся на тему программирования и всего что с ним связано 👊\n\n🕒 Все стикеры и медиа временно запрещены\n🤬 Ненормативная лексика запрещена\n👽 Оффтоп наказывается мутами\n\nПриятного общения 🥰", + "every": 10800 }, { - "message" : "#оставайтесьдома 👾 играйте в игры, 👽 смотрите фильмы, 😴 больше отдыхайте.\n\n✌️ Будьте здоровы", - "every" : 7200 + "message": "#оставайтесьдома 👾 играйте в игры, 👽 смотрите фильмы, 😴 больше отдыхайте.\n\n✌️ Будьте здоровы", + "every": 7200 } ) }, diff --git a/requirements.txt b/requirements.txt index 75d9f98..21af16b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ aiogram asyncio aioschedule psutil -easydict \ No newline at end of file +easydict +asyncpg \ No newline at end of file diff --git a/runtime.txt b/runtime.txt index 396db58..333d904 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -python-3.9.6 \ No newline at end of file +python-3.10.0 \ No newline at end of file diff --git a/utils.py b/utils.py index 8c69d87..6036f7d 100644 --- a/utils.py +++ b/utils.py @@ -12,6 +12,7 @@ censor_ru = Censor.get(lang='ru') censor_en = Censor.get(lang='en') + def check_for_profanity(text, lang="ru"): _profanity_detected = False _word = None