diff --git a/.gitignore b/.gitignore
index 64c90ba..851d7b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -147,4 +147,4 @@ database.db
.idea/misc.xml
.idea/modules.xml
.idea/sqldialects.xml
-.idea/vcs.xml
+.idea/vcs.xml
\ No newline at end of file
diff --git a/BotBase/__init__.py b/BotBase/__init__.py
index 1424684..fc2d656 100644
--- a/BotBase/__init__.py
+++ b/BotBase/__init__.py
@@ -1 +1 @@
-__version__ = (2, 0, 0)
+__version__ = (2, 1, 0)
diff --git a/BotBase/config.example.py b/BotBase/config.example.py
deleted file mode 100644
index 2318d9a..0000000
--- a/BotBase/config.example.py
+++ /dev/null
@@ -1,239 +0,0 @@
-"""
-Copyright 2020 Nocturn9x, alsoGAMER, CrisMystik
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-import os
-import re
-from collections import defaultdict
-
-from pyrogram import Client, filters
-from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
-
-# Antiflood module configuration
-# The antiflood works by accumulating up to MAX_UPDATE_THRESHOLD updates (user-wise) and
-# when that limit is reached, perform some checks to tell if the user is actually flooding
-
-BAN_TIME = 300
-# The amount of seconds the user will be banned
-MAX_UPDATE_THRESHOLD = 7
-# How many updates to accumulate before starting to count
-PRIVATE_ONLY = True
-# If True, the antiflood will only work in private chats
-FLOOD_PERCENTAGE = 75
-# The percentage (from 0 to 100) of updates that when below ANTIFLOOD_SENSIBILITY will trigger the anti flood
-# Example, if FLOOD_PERCENTAGE == 75, if at least 75% of the messages from a user are marked as flood it will be blocked
-ANTIFLOOD_SENSIBILITY = 1
-# The minimum amount of seconds between updates. Updates that are sent faster than this limit will trigger the antiflood
-# This should not be below 1, but you can experiment if you feel bold enough
-FLOOD_NOTICE = f"š¤ Hey buddy!\nš Relax! You have been banned for {BAN_TIME / 60:.1f} minutes"
-# If you want the user to be notified of being flood-blocked, set this to the desired message, False to disable
-FLOOD_CLEARED = "ā»ļø Antiflood table cleaned up"
-FLOOD_USER_CLEARED = "ā»ļø Antiflood table for{user}
cleaned up"
-DELETE_MESSAGES = True
-# Set this to false if you do not want the messages to be deleted after flood is detected
-
-# Various options and global variables
-
-CACHE = defaultdict(lambda: ["none", 0])
-# Global cache. DO NOT TOUCH IT, really just don't
-VERSION = "2.0.1a"
-RELEASE_DATE = "05/12/2020"
-CREDITS = "āš» Bot developed by @yourusernamehere in Python3.x and BotBase 2.0.1" \
- f"\nāļø Version: {VERSION}
\nš Release date: {RELEASE_DATE}
"
-# These will be shown in the 'Credits' section
-
-# Telegram client configuration
-
-WORKERS_NUM = 15
-# The number of worker threads that pyrogram will spawn at the startup.
-# 15 workers means that the bot will process up to 15 users at the same time and then block until one worker has done
-BOT_TOKEN = "BOT_TOKEN_HERE"
-# Get it with t.me/BotFather
-SESSION_NAME = "BotBase"
-# The name of the Telegram Session that the bot will have, will be visible from Telegram
-PLUGINS_ROOT = {"root": f"BotBase/modules"}
-# Do not change this unless you know what you're doing
-API_ID = 123456
-# Get it at https://my.telegram.org/apps
-API_HASH = "API_HASH_HERE"
-# Same as above
-DEVICE_MODEL = "BotBase"
-# Name of the device shown in the sessions list - useless for a Bot
-SYSTEM_VERSION = "2.0.1a"
-# Host OS version, can be the same as VERSION - also useless for a Bot
-LANG_CODE = "en_US"
-# Session lang_code
-
-# Logging configuration
-# To know more about what these options mean, check https://docs.python.org/3/library/logging.html
-
-LOGGING_FORMAT = "[%(levelname)s %(asctime)s] In thread '%(threadName)s', " \
- f"module %(module)s, function %(funcName)s at line %(lineno)d -> [{SESSION_NAME}] %(message)s"
-DATE_FORMAT = "%d/%m/%Y %H:%M:%S %p"
-LOGGING_LEVEL = 30
-bot = Client(api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN, plugins=PLUGINS_ROOT,
- session_name=SESSION_NAME, workers=WORKERS_NUM, device_model=DEVICE_MODEL, system_version=SYSTEM_VERSION,
- lang_code=LANG_CODE)
-
-# Start module
-# P.S.: {mention} in the GREET message will be replaced with a mention to the user, same applies for {id} and {username}
-
-GREET = """š Hi {mention} and welcome to BotBase."""
-# The message that will be sent as a reply to the /start command. If this string is empty the bot will not reply.
-SUPPORT_BUTTON = "š Start Chat"
-# The text for the button that triggers the live chat
-BACK_BUTTON = "š Back"
-# The text for the 'Back' button
-CREDITS_BUTTON = "ā¹ Credits"
-# The text for the 'Credits' button
-BUTTONS = InlineKeyboardMarkup([
- [InlineKeyboardButton(SUPPORT_BUTTON, "begin_chat"),
- InlineKeyboardButton(CREDITS_BUTTON, "bot_info")]])
-# This keyboard will be sent along with GREET, feel free to add or remove buttons
-
-# Database configuration
-# The only natively supported database is SQLite3, but you can easily tweak
-# this section and the BotBase/database/query.py file to work with any DBMS
-# If you do so and want to share your code feel free to open a PR on the repo!
-
-DB_PATH = os.path.join(os.getcwd(), f"BotBase/database/database.db")
-DB_CREATE = """CREATE TABLE IF NOT EXISTS users (
- tg_id INTEGER PRIMARY KEY NOT NULL,
- tg_uname TEXT UNIQUE NULL DEFAULT 'null',
- date TEXT NOT NULL,
- banned INTEGER NOT NULL DEFAULT 0);
- """
-
-DB_GET_USERS = "SELECT tg_id FROM users"
-DB_GET_USER = "SELECT * FROM users where users.tg_id = ?"
-DB_SET_USER = "INSERT INTO users (tg_id, tg_uname, date, banned) VALUES(?, ?, ?, ?)"
-DB_BAN_USER = "UPDATE users SET banned = 1 WHERE users.tg_id = ?"
-DB_UNBAN_USER = "UPDATE users SET banned = 0 WHERE users.tg_id = ?"
-DB_CHECK_BANNED = "SELECT banned FROM users WHERE users.tg_id = ?"
-DB_UPDATE_NAME = "UPDATE users SET tg_uname = ? WHERE users.tg_id = ?"
-DB_GET_USER_BY_NAME = "SELECT * FROM users where users.tg_uname = ?"
-from BotBase.database.query import check_banned
-
-# Admin module configuration
-
-ADMINS = {1234567: "Lorem Ipsum"}
-# Edit this dict adding the ID:NAME pair of the admin that you want to add. You can add as many admins as you want.
-MARKED_BUSY = "š² You're now busy, resend /busy to reset this state"
-UNMARKED_BUSY = "ā You'll now receive support requests again"
-CANNOT_BAN_ADMIN = "ā Error: This user is an administrator"
-USER_BANNED = "ā
User banned successfully"
-USER_UNBANNED = "ā
User unbanned successfully"
-YOU_ARE_UNBANNED = "ā
You've been unbanned"
-USER_NOT_BANNED = "ā This user isn't banned"
-CLOSE_CHAT_BUTTON = "ā Close chat"
-UPDATE_BUTTON = "š Update"
-USER_ALREADY_BANNED = "ā This user is already banned"
-YOU_ARE_BANNED = "ā You've been banned"
-WHISPER_FROM = "š£ Message from {admin}: {msg}"
-WHISPER_SUCCESSFUL = "ā
Sent successfully"
-NAME = "tg://user?id={}"
-BYPASS_FLOOD = True
-# If False, admins can be flood-blocked too, otherwise the antiflood will ignore them
-USER_INFO_UPDATED = "ā
Information updated"
-USER_INFO_UNCHANGED = "ā I haven't detected any changes for this user"
-ADMIN_ACCEPTED_CHAT = "ā
{admin} has joined the chat with {user}"
-USER_LEFT_QUEUE = "ā ļø {user} left the queue"
-QUEUE_LIST = "š» List of users waiting\n\n{queue}"
-CHATS_LIST = "š¬ List of users in chat\n\n{chats}"
-ADMIN_BUSY = "(Busy)"
-USER_INFO = """ā¹ļø User infos
-
-š ID: {tg_id}
-āļø Username: {tg_uname}
-š Registered on: {date}
-āØļø Banned: {status}
-š” Admin: {admin}
"""
-# The message that is sent with /getuser and /getranduser
-INVALID_SYNTAX = "ā Invalid syntax: Use {correct}
"
-# This is sent when a command is used the wrong way
-ERROR = "ā Error"
-# This is sent when a command returns an error
-NON_NUMERIC_ID = "The ID must be numeric!"
-# This is sent if the parameter to /getuser is not a numerical ID
-USERS_COUNT = "Total users: {count}
"
-# This is sent as a result of the /count command
-NO_PARAMETERS = "ā {command}
requires no parameters"
-# Error saying that the given command takes no parameters
-ID_MISSING = "The selected ID ({tg_id}
) isn't in the database"
-# Error when given ID is not in database
-NAME_MISSING = "The selected username ({tg_uname}
) isn't in the database"
-# Error when given username is not in database
-YES = "Yes"
-NO = "No"
-GLOBAL_MESSAGE_STATS = """Message Statistics
-
-āļø Message: {msg}
-š Attempts: {count}
-ā
Delivered: {success}
"""
-# Statistics that are sent to the admin after /global command
-
-# Live chat configuration
-
-ADMINS_LIST_UPDATE_DELAY = 30
-# How many seconds between an update and another
-LIVE_CHAT_STATUSES = "Legend: š¢ = Available, š“ = Busy\n\n"
-SUPPORT_NOTIFICATION = "š New support request!\n\n{uinfo}"
-ADMIN_JOINS_CHAT = " [{admin_name}]({admin_id}) joined the chat!"
-USER_CLOSES_CHAT = "š [{user_name}]({user_id}) closed the chat"
-USER_LEAVES_CHAT = "ā
You left the chat"
-USER_JOINS_CHAT = "ā
You've joined the chat"
-CHAT_BUSY = "ā ļø Another admin has already joined"
-LEAVE_CURRENT_CHAT = "ā ļø Close current chat first!"
-CANNOT_REQUEST_SUPPORT = "ā ļø You can't start a chat!"
-STATUS_FREE = "š¢ "
-STATUS_BUSY = "š“ "
-SUPPORT_REQUEST_SENT = "ā
You're now in the queue, wait for an admin to answer you\n\n" \
- "š Admins available\n{queue}\nUpdated on: {date}
\n\nNote: " \
- "If there are no admins available at the moment, press the 'Update' button every now and " \
- "then to find out if a seat is available!"
-JOIN_CHAT_BUTTON = "ā Join the chat"
-USER_MESSAGE = "š£ [{user_name}]({user_id}): {message}"
-ADMIN_MESSAGE = "š§āš» [{user_name}]({user_id}): {message}"
-TOO_FAST = "ā Calm down! Try again later"
-
-
-# Custom filters - Don't touch them as well but feel free to add more!
-
-def check_user_banned(tg_id: int):
- res = check_banned(tg_id)
- if isinstance(res, Exception):
- return False
- else:
- if not res:
- return False
- return bool(res[0])
-
-
-def callback_regex(pattern: str):
- return filters.create(lambda flt, client, update: re.match(pattern, update.data))
-
-
-def admin_is_chatting():
- return filters.create(
- lambda flt, client, update: update.from_user.id in ADMINS and CACHE[update.from_user.id][0] == "IN_CHAT")
-
-
-def user_is_chatting():
- return filters.create(
- lambda flt, client, update: update.from_user.id not in ADMINS and CACHE[update.from_user.id][0] == "IN_CHAT")
-
-
-def user_banned():
- return filters.create(lambda flt, client, update: check_user_banned(update.from_user.id))
diff --git a/BotBase/config.sample.py b/BotBase/config.sample.py
new file mode 100644
index 0000000..ec34585
--- /dev/null
+++ b/BotBase/config.sample.py
@@ -0,0 +1,146 @@
+"""
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+from collections import defaultdict
+
+import MySQLdb
+from pyrogram import Client
+from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
+
+# region Antiflood module configuration
+# The antiflood works by accumulating up to MAX_UPDATE_THRESHOLD updates (user-wise) and
+# when that limit is reached, perform some checks to tell if the user is actually flooding
+
+BAN_TIME = 300
+# The amount of seconds the user will be banned
+MAX_UPDATE_THRESHOLD = 7
+# How many updates to accumulate before starting to count
+PRIVATE_ONLY = True
+# If True, the antiflood will only work in private chats
+FLOOD_PERCENTAGE = 75
+# The percentage (from 0 to 100) of updates that when below ANTIFLOOD_SENSIBILITY will trigger the anti flood
+# Example, if FLOOD_PERCENTAGE == 75, if at least 75% of the messages from a user are marked as flood it will be blocked
+ANTIFLOOD_SENSIBILITY = 1
+# The minimum amount of seconds between updates. Updates that are sent faster than this limit will trigger the antiflood
+# This should not be below 1, but you can experiment if you feel bold enough
+DELETE_MESSAGES = True
+# Set this to false if you do not want the messages to be deleted after flood is detected
+# endregion
+
+# region Various options and global variables
+
+CACHE = defaultdict(lambda: ["none", 0])
+# Global cache. DO NOT TOUCH IT, really just don't
+VERSION = ""
+RELEASE_DATE = ""
+CREDITS = "āš» Bot developed by @yourusernamehere in Python3.9 and BotBase 2.1.0" \
+ f"\nāļø Version: {VERSION}
\nš Release Date: {RELEASE_DATE}
"
+# These will be shown in the 'Credits' section
+# endregion
+
+# region Telegram client configuration
+
+WORKERS_NUM = 15
+# The number of worker threads that pyrogram will spawn at the startup.
+# 15 workers means that the bot will process up to 15 users at the same time and then block until one worker has done
+
+BOT_TOKEN = ""
+# Get it with t.me/BotFather
+SESSION_NAME = ""
+# The name of the Telegram Session that the bot will have, will be visible from Telegram
+PLUGINS_ROOT = {"root": f"BotBase/modules"}
+# Do not change this unless you know what you're doing
+API_ID = 000000
+# Get it at https://my.telegram.org/apps
+API_HASH = ""
+# Same as above
+DEVICE_MODEL = ""
+# Name of the device shown in the sessions list - useless for a Bot
+SYSTEM_VERSION = ""
+# Host OS version, can be the same as VERSION - also useless for a Bot
+LANG_CODE = "en_US"
+# Session lang_code
+# endregion
+
+# region Logging configuration
+# To know more about what these options mean, check https://docs.python.org/3/library/logging.html
+
+LOGGING_FORMAT = "[%(levelname)s %(asctime)s] In thread '%(threadName)s', " \
+ f"module %(module)s, function %(funcName)s at line %(lineno)d -> [{SESSION_NAME}] %(message)s"
+DATE_FORMAT = "%d/%m/%Y %H:%M:%S %p"
+LOGGING_LEVEL = 30
+bot = Client(api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN, plugins=PLUGINS_ROOT,
+ session_name=SESSION_NAME, workers=WORKERS_NUM, device_model=DEVICE_MODEL, system_version=SYSTEM_VERSION,
+ lang_code=LANG_CODE)
+# endregion
+
+# region Start module
+# P.S.: {mention} in the GREET message will be replaced with a mention to the user, same applies for {id} and {username}
+
+GREET = "š Hi {mention} and welcome to BotBase."
+# The message that will be sent to the users as a reply to the /start command. If this string is empty the bot will not reply.
+# endregion
+
+# region Database configuration
+# The only natively supported database is MariaDB, but you can easily tweak
+# this section and the BotBase/database/query.py file to work with any DBMS
+# If you do so and want to share your code feel free to open a PR on the repo!
+
+DB_URL = MySQLdb.connect(host="host", user="user", passwd="passwd", db="db")
+# endregion
+
+# region Greet Keyboard
+from BotBase.strings.default_strings import SUPPORT_BUTTON, ABOUT_BUTTON
+BUTTONS = InlineKeyboardMarkup([
+ [InlineKeyboardButton(SUPPORT_BUTTON, "begin_chat"),
+ InlineKeyboardButton(ABOUT_BUTTON, "bot_about")]])
+# This keyboard will be sent along with GREET, feel free to add or remove buttons
+# endregion
+
+# region Admin module configuration
+
+ADMINS = {00000000: "A Dude"}
+# Edit this dict adding the ID:NAME pair of the admin that you want to add. You can add as many admins as you want.
+
+NAME = "tg://user?id={}"
+BYPASS_FLOOD = True
+# If False, admins can be flood-blocked too, otherwise the antiflood will ignore them
+USER_INFO = """ā¹ļø User info
+
+š ID: {tg_id}
+āļø Username: {tg_uname}
+š Registered on: {date}
+āØļø Banned: {status}
+š” Admin: {admin}
"""
+# The message that is sent with /getuser and /getranduser
+GLOBAL_MESSAGE_STATS = """Message Statistics
+
+āļø Message: {msg}
+š Attempts: {count}
+ā
Delivered: {success}
"""
+# Statistics that are sent to the admin after /global command
+# endregion
+
+# region Live chat configuration
+
+ADMINS_LIST_UPDATE_DELAY = 30
+# How many seconds between an update and another
+LIVE_CHAT_STATUSES = "Legend: š¢ = Available, š“ = Busy\n\n"
+STATUS_FREE = "š¢ "
+STATUS_BUSY = "š“ "
+USER_MESSAGE = "š£ [{user_name}]({user_id}): {message}"
+ADMIN_MESSAGE = "š§āš» [{user_name}]({user_id}): {message}"
+# endregion
diff --git a/BotBase/database/__init__.py b/BotBase/database/__init__.py
index 1424684..fc2d656 100644
--- a/BotBase/database/__init__.py
+++ b/BotBase/database/__init__.py
@@ -1 +1 @@
-__version__ = (2, 0, 0)
+__version__ = (2, 1, 0)
diff --git a/BotBase/database/query.py b/BotBase/database/query.py
index 2afc06e..75928ec 100644
--- a/BotBase/database/query.py
+++ b/BotBase/database/query.py
@@ -1,5 +1,5 @@
"""
-Copyright 2020 Nocturn9x, alsoGAMER, CrisMystik
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,150 +14,150 @@
limitations under the License.
"""
-import sqlite3.dbapi2 as sqlite3
-from BotBase.config import DB_GET_USERS, DB_GET_USER, DB_PATH, DB_SET_USER, DB_UPDATE_NAME, DB_BAN_USER, DB_UNBAN_USER, \
- DB_GET_USER_BY_NAME, DB_CHECK_BANNED
import logging
import time
-import os
+import MySQLdb
-def create_database(path: str, query: str):
- if os.path.exists(path):
- logging.warning(f"Database file exists at {path}, running query")
- else:
- logging.warning(f"No database found, creating it at {path}")
+from BotBase.config import DB_URL
+from BotBase.database.raw_queries import *
+
+
+def create_table(query: str):
try:
- database = sqlite3.connect(path)
- except sqlite3.Error as connection_error:
+ database = DB_URL.cursor()
+ except MySQLdb.DatabaseError as connection_error:
logging.error(f"An error has occurred while connecting to database: {connection_error}")
else:
try:
with database:
- database.executescript(query)
+ database.execute(query)
+ DB_URL.commit()
database.close()
- except sqlite3.Error as query_error:
- logging.info(f"An error has occurred while executing DB_CREATE: {query_error}")
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as query_error:
+ logging.info(f"An error has occurred while executing CREATE_TABLE: {query_error}")
def get_user(tg_id: int):
try:
- database = sqlite3.connect(DB_PATH)
- except sqlite3.Error as connection_error:
+ database = DB_URL.cursor()
+ except MySQLdb.DatabaseError as connection_error:
logging.error(f"An error has occurred while connecting to database: {connection_error}")
else:
try:
with database:
- cursor = database.cursor()
- query = cursor.execute(DB_GET_USER, (tg_id, ))
- return query.fetchone()
- except sqlite3.Error as query_error:
+ database.execute(DB_GET_USER, (tg_id,))
+ return database.fetchone()
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as query_error:
logging.error(f"An error has occurred while executing DB_GET_USER query: {query_error}")
return query_error
def get_user_by_name(tg_uname: str):
try:
- database = sqlite3.connect(DB_PATH)
- except sqlite3.Error as connection_error:
+ database = DB_URL.cursor()
+ except MySQLdb.DatabaseError as connection_error:
logging.error(f"An error has occurred while connecting to database: {connection_error}")
else:
try:
with database:
- cursor = database.cursor()
- query = cursor.execute(DB_GET_USER_BY_NAME, (tg_uname, ))
- return query.fetchone()
- except sqlite3.Error as query_error:
+ database.execute(DB_GET_USER_BY_NAME, (tg_uname,))
+ return database.fetchone()
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as query_error:
logging.error(f"An error has occurred while executing DB_GET_USER_BY_NAME query: {query_error}")
return query_error
def update_name(tg_id: int, name: str):
try:
- database = sqlite3.connect(DB_PATH)
- except sqlite3.Error as connection_error:
+ database = DB_URL.cursor()
+ except MySQLdb.DatabaseError as connection_error:
logging.error(f"An error has occurred while connecting to database: {connection_error}")
else:
try:
with database:
database.execute(DB_UPDATE_NAME, (name, tg_id))
+ DB_URL.commit()
return True
- except sqlite3.Error as query_error:
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as query_error:
logging.error(f"An error has occurred while executing DB_UPDATE_NAME query: {query_error}")
return query_error
def get_users():
try:
- database = sqlite3.connect(DB_PATH)
- except sqlite3.Error as connection_error:
+ database = DB_URL.cursor()
+ except MySQLdb.DatabaseError as connection_error:
logging.error(f"An error has occurred while connecting to database: {connection_error}")
else:
try:
with database:
- cursor = database.cursor()
- query = cursor.execute(DB_GET_USERS)
- return query.fetchall()
- except sqlite3.Error as query_error:
+ database.execute(DB_GET_USERS)
+ return database.fetchall()
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as query_error:
logging.error(f"An error has occurred while executing DB_GET_USERS query: {query_error}")
return query_error
def set_user(tg_id: int, tg_uname: str):
try:
- database = sqlite3.connect(DB_PATH)
- except sqlite3.Error as connection_error:
+ database = DB_URL.cursor()
+ except MySQLdb.DatabaseError as connection_error:
logging.error(f"An error has occurred while connecting to database: {connection_error}")
else:
try:
with database:
- database.execute(DB_SET_USER, (tg_id, tg_uname, time.strftime("%d/%m/%Y %T %p"), 0, 0, 0))
+ database.execute(DB_SET_USER,
+ (tg_id, tg_uname, time.strftime("%d/%m/%Y %T %p"), 0))
+ DB_URL.commit()
return True
- except sqlite3.Error as query_error:
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as query_error:
logging.error(f"An error has occurred while executing DB_SET_USER query: {query_error}")
return query_error
def ban_user(tg_id: int):
try:
- database = sqlite3.connect(DB_PATH)
- except sqlite3.Error as connection_error:
+ database = DB_URL.cursor()
+ except MySQLdb.DatabaseError as connection_error:
logging.error(f"An error has occurred while connecting to database: {connection_error}")
else:
try:
with database:
- database.execute(DB_BAN_USER, (tg_id, ))
+ database.execute(DB_BAN_USER, (tg_id,))
+ DB_URL.commit()
return True
- except sqlite3.Error as query_error:
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as query_error:
logging.error(f"An error has occurred while executing DB_BAN_USER query: {query_error}")
return query_error
def unban_user(tg_id: int):
try:
- database = sqlite3.connect(DB_PATH)
- except sqlite3.Error as connection_error:
+ database = DB_URL.cursor()
+ except MySQLdb.DatabaseError as connection_error:
logging.error(f"An error has occurred while connecting to database: {connection_error}")
else:
try:
with database:
- database.execute(DB_UNBAN_USER, (tg_id, ))
+ database.execute(DB_UNBAN_USER, (tg_id,))
+ DB_URL.commit()
return True
- except sqlite3.Error as query_error:
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as query_error:
logging.error(f"An error has occurred while executing DB_UNBAN_USER query: {query_error}")
return query_error
def check_banned(tg_id: int):
try:
- database = sqlite3.connect(DB_PATH)
- except sqlite3.Error as connection_error:
+ database = DB_URL.cursor()
+ except MySQLdb.DatabaseError as connection_error:
logging.error(f"An error has occurred while connecting to database: {connection_error}")
else:
try:
with database:
- query = database.execute(DB_CHECK_BANNED, (tg_id, ))
- return query.fetchone()
- except sqlite3.Error as query_error:
+ database.execute(DB_CHECK_BANNED, (tg_id,))
+ return database.fetchone()
+ except (MySQLdb.ProgrammingError, MySQLdb.OperationalError) as query_error:
logging.error(f"An error has occurred while executing DB_CHECK_BANNED query: {query_error}")
return query_error
diff --git a/BotBase/database/raw_queries.py b/BotBase/database/raw_queries.py
new file mode 100644
index 0000000..531708e
--- /dev/null
+++ b/BotBase/database/raw_queries.py
@@ -0,0 +1,34 @@
+"""
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# region CreateTablesQueries
+CREATE_USERS_TABLE = """CREATE TABLE IF NOT EXISTS users (
+ tg_id INTEGER(20) PRIMARY KEY NOT NULL,
+ tg_uname VARCHAR(32) UNIQUE NULL DEFAULT 'null',
+ date TEXT NOT NULL,
+ banned INTEGER NOT NULL DEFAULT 0);
+ """
+# endregion
+
+
+DB_GET_USERS = "SELECT users.tg_id FROM users"
+DB_GET_USER = "SELECT * FROM users WHERE users.tg_id = %s"
+DB_SET_USER = "INSERT INTO users (tg_id, tg_uname, date, banned) VALUES (%s, %s, %s, %s)"
+DB_BAN_USER = "UPDATE users SET users.banned = FALSE WHERE users.tg_id = %s"
+DB_UNBAN_USER = "UPDATE users SET users.banned = FALSE WHERE users.tg_id = %s"
+DB_CHECK_BANNED = "SELECT users.banned FROM users WHERE users.tg_id = %s"
+DB_UPDATE_NAME = "UPDATE users SET users.tg_uname = %s WHERE users.tg_id = %s"
+DB_GET_USER_BY_NAME = "SELECT * FROM users WHERE users.tg_uname = %s"
diff --git a/BotBase/methods/__init__.py b/BotBase/methods/__init__.py
index a9222bc..47c7d56 100644
--- a/BotBase/methods/__init__.py
+++ b/BotBase/methods/__init__.py
@@ -1,2 +1,2 @@
-__version__ = (2, 0, 0)
+__version__ = (2, 1, 0)
from ._wrapper import MethodWrapper
diff --git a/BotBase/methods/_wrapper.py b/BotBase/methods/_wrapper.py
index 241f549..c3c8480 100644
--- a/BotBase/methods/_wrapper.py
+++ b/BotBase/methods/_wrapper.py
@@ -1,5 +1,5 @@
"""
-Copyright 2020 Nocturn9x, alsoGAMER, CrisMystik
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -51,4 +51,5 @@ async def wrapper(*args, **kwargs):
return rpc_error
else:
raise AttributeError(self.instance, attribute)
+
return wrapper
diff --git a/BotBase/methods/custom_filters.py b/BotBase/methods/custom_filters.py
new file mode 100644
index 0000000..ffc167f
--- /dev/null
+++ b/BotBase/methods/custom_filters.py
@@ -0,0 +1,50 @@
+"""
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import re
+
+from pyrogram import filters
+
+from BotBase.config import ADMINS, CACHE
+from BotBase.database.query import check_banned
+
+
+def check_user_banned(tg_id: int):
+ res = check_banned(tg_id)
+ if isinstance(res, Exception):
+ return False
+ else:
+ if not res:
+ return False
+ return bool(res[0])
+
+
+def callback_regex(pattern: str):
+ return filters.create(lambda flt, client, update: re.match(pattern, update.data))
+
+
+def admin_is_chatting():
+ return filters.create(
+ lambda flt, client, update: update.from_user.id in ADMINS and CACHE[update.from_user.id][0] == "IN_CHAT")
+
+
+def user_is_chatting():
+ return filters.create(
+ lambda flt, client, update: update.from_user.id not in ADMINS and CACHE[update.from_user.id][0] == "IN_CHAT")
+
+
+def user_banned():
+ return filters.create(lambda flt, client, update: check_user_banned(update.from_user.id))
diff --git a/BotBase/modules/__init__.py b/BotBase/modules/__init__.py
index 1424684..fc2d656 100644
--- a/BotBase/modules/__init__.py
+++ b/BotBase/modules/__init__.py
@@ -1 +1 @@
-__version__ = (2, 0, 0)
+__version__ = (2, 1, 0)
diff --git a/BotBase/modules/admin.py b/BotBase/modules/admin.py
index 43f89fa..cfdab90 100644
--- a/BotBase/modules/admin.py
+++ b/BotBase/modules/admin.py
@@ -1,5 +1,5 @@
"""
-Copyright 2020 Nocturn9x, alsoGAMER, CrisMystik
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -21,11 +21,11 @@
from pyrogram import Client, filters
-from BotBase.config import ADMINS, CACHE, CANNOT_BAN_ADMIN, CHATS_LIST, ERROR, \
- GLOBAL_MESSAGE_STATS, ID_MISSING, INVALID_SYNTAX, LEAVE_CURRENT_CHAT, MARKED_BUSY, NAME, NAME_MISSING, NO, \
- NON_NUMERIC_ID, NO_PARAMETERS, QUEUE_LIST, UNMARKED_BUSY, USERS_COUNT, USER_ALREADY_BANNED, \
- USER_BANNED, USER_INFO, USER_INFO_UNCHANGED, USER_INFO_UPDATED, USER_NOT_BANNED, USER_UNBANNED, \
- WHISPER_FROM, WHISPER_SUCCESSFUL, YES, YOU_ARE_BANNED, YOU_ARE_UNBANNED, bot
+from BotBase.config import ADMINS, CACHE, GLOBAL_MESSAGE_STATS, NAME, USER_INFO, bot
+from BotBase.strings.default_strings import CANNOT_BAN_ADMIN, CHATS_LIST, ERROR, ID_MISSING, INVALID_SYNTAX, \
+ LEAVE_CURRENT_CHAT, MARKED_BUSY, NAME_MISSING, NO, NON_NUMERIC_ID, NO_PARAMETERS, QUEUE_LIST, UNMARKED_BUSY, \
+ USERS_COUNT, USER_ALREADY_BANNED, USER_BANNED, USER_INFO_UNCHANGED, USER_INFO_UPDATED, USER_NOT_BANNED, \
+ USER_UNBANNED, WHISPER_FROM, WHISPER_SUCCESSFUL, YES, YOU_ARE_BANNED, YOU_ARE_UNBANNED
from BotBase.database.query import ban_user, get_user, get_user_by_name, get_users, unban_user, update_name
from BotBase.methods import MethodWrapper
from BotBase.modules.antiflood import BANNED_USERS
@@ -38,10 +38,10 @@ def format_user(user):
tg_id, tg_uname, date, banned = user
return USER_INFO.format(
tg_id=tg_id,
- tg_uname='@' + tg_uname if tg_uname else 'null',
+ tg_uname='@' + tg_uname if tg_uname else 'N/A',
date=date,
status=YES if banned else NO,
- admin=YES if tg_id in ADMINS else NO
+ admin=YES if tg_id in ADMINS else NO,
)
@@ -230,7 +230,7 @@ async def ban(_, message):
else:
await wrapper.send_message(message.chat.id, USER_NOT_BANNED if condition else USER_ALREADY_BANNED)
else:
- await wrapper.send_message(message.chat.id, f"{ERROR}: {ID_MISSING.format(uid=message.command[1])}")
+ await wrapper.send_message(message.chat.id, f"{ERROR}: {ID_MISSING.format(tg_id=message.command[1])}")
@Client.on_message(filters.command("busy") & ADMINS_FILTER & filters.private & ~BANNED_USERS & ~filters.edited)
diff --git a/BotBase/modules/antiflood.py b/BotBase/modules/antiflood.py
index 73329ad..92da68e 100644
--- a/BotBase/modules/antiflood.py
+++ b/BotBase/modules/antiflood.py
@@ -1,5 +1,5 @@
"""
-Copyright 2020 Nocturn9x, alsoGAMER, CrisMystik
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,15 +14,16 @@
limitations under the License.
"""
-from collections import defaultdict
import logging
import time
+from collections import defaultdict
from pyrogram import Client, filters
-from BotBase.config import ADMINS, ANTIFLOOD_SENSIBILITY, BAN_TIME, BYPASS_FLOOD, CACHE, DELETE_MESSAGES, ERROR, \
- FLOOD_CLEARED, FLOOD_NOTICE, FLOOD_PERCENTAGE, FLOOD_USER_CLEARED, MAX_UPDATE_THRESHOLD, NON_NUMERIC_ID, \
- PRIVATE_ONLY, bot, user_banned
+from BotBase.config import ADMINS, ANTIFLOOD_SENSIBILITY, BAN_TIME, BYPASS_FLOOD, CACHE, DELETE_MESSAGES, \
+ FLOOD_PERCENTAGE, MAX_UPDATE_THRESHOLD, PRIVATE_ONLY, bot
+from BotBase.strings.default_strings import ERROR, FLOOD_CLEARED, FLOOD_NOTICE, FLOOD_USER_CLEARED, NON_NUMERIC_ID
+from BotBase.methods.custom_filters import user_banned
from BotBase.methods import MethodWrapper
# Some variables for runtime configuration
diff --git a/BotBase/modules/livechat.py b/BotBase/modules/livechat.py
index 380c0e6..22a1520 100644
--- a/BotBase/modules/livechat.py
+++ b/BotBase/modules/livechat.py
@@ -1,5 +1,5 @@
"""
-Copyright 2020 Nocturn9x, alsoGAMER, CrisMystik
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -20,15 +20,16 @@
from pyrogram import Client, filters
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
-from BotBase.modules.antiflood import BANNED_USERS
-from BotBase.modules.start import cb_start_handler
-from BotBase.config import CACHE, ADMINS, ADMINS_LIST_UPDATE_DELAY, callback_regex, admin_is_chatting, \
- user_is_chatting, LIVE_CHAT_STATUSES, STATUS_BUSY, STATUS_FREE, SUPPORT_REQUEST_SENT, SUPPORT_NOTIFICATION, \
- ADMIN_JOINS_CHAT, USER_CLOSES_CHAT, JOIN_CHAT_BUTTON, USER_INFO, USER_LEAVES_CHAT, ADMIN_MESSAGE, USER_MESSAGE, \
- TOO_FAST, CHAT_BUSY, LEAVE_CURRENT_CHAT, USER_JOINS_CHAT, NAME, CANNOT_REQUEST_SUPPORT, YES, NO, user_banned, \
- CLOSE_CHAT_BUTTON, BACK_BUTTON, UPDATE_BUTTON, bot, ADMIN_ACCEPTED_CHAT
+from BotBase.config import CACHE, ADMINS, ADMINS_LIST_UPDATE_DELAY, LIVE_CHAT_STATUSES, STATUS_BUSY, STATUS_FREE, \
+ USER_INFO, ADMIN_MESSAGE, USER_MESSAGE, NAME, bot
+from BotBase.strings.default_strings import SUPPORT_REQUEST_SENT, SUPPORT_NOTIFICATION, ADMIN_JOINS_CHAT, \
+ USER_CLOSES_CHAT, JOIN_CHAT_BUTTON, USER_LEAVES_CHAT, TOO_FAST, CHAT_BUSY, LEAVE_CURRENT_CHAT, USER_JOINS_CHAT, \
+ CANNOT_REQUEST_SUPPORT, YES, NO, CLOSE_CHAT_BUTTON, BACK_BUTTON, UPDATE_BUTTON, ADMIN_ACCEPTED_CHAT
+from BotBase.methods.custom_filters import callback_regex, admin_is_chatting, user_is_chatting, user_banned
from BotBase.database.query import get_user
from BotBase.methods import MethodWrapper
+from BotBase.modules.antiflood import BANNED_USERS
+from BotBase.modules.start import cb_start_handler
ADMINS_FILTER = filters.user(list(ADMINS.keys()))
BUTTONS = InlineKeyboardMarkup(
@@ -54,15 +55,18 @@ async def begin_chat(_, query):
else:
queue += f"- {STATUS_BUSY}"
queue += f"[{admin_name}]({NAME.format(admin_id)})\n"
- msg = await cb_wrapper.edit_message_text(SUPPORT_REQUEST_SENT.format(queue=queue, date=time.strftime('%d/%m/%Y %T')),
- reply_markup=BUTTONS)
+ msg = await cb_wrapper.edit_message_text(
+ SUPPORT_REQUEST_SENT.format(queue=queue, date=time.strftime('%d/%m/%Y %T')),
+ reply_markup=BUTTONS)
join_chat_button = InlineKeyboardMarkup(
[[InlineKeyboardButton(JOIN_CHAT_BUTTON, f"join_{query.from_user.id}")]])
user = get_user(query.from_user.id)
tg_id, tg_uname, date, banned = user
- text = USER_INFO.format(tg_id=tg_id, tg_uname='@' + tg_uname if tg_uname else 'null', date=date,
+ text = USER_INFO.format(tg_id=tg_id,
+ tg_uname='@' + tg_uname if tg_uname else 'N/A',
+ date=date,
status=YES if banned else NO,
- admin='N/A')
+ admin=YES if tg_id in ADMINS else NO)
CACHE[query.from_user.id].append([])
for admin in ADMINS:
status = CACHE[admin][0]
@@ -88,15 +92,18 @@ async def update_admins_list(_, query):
else:
queue += f"- {STATUS_BUSY}"
queue += f"[{admin_name}]({NAME.format(admin_id)})\n"
- await cb_wrapper.edit_message_text(SUPPORT_REQUEST_SENT.format(queue=queue, date=time.strftime('%d/%m/%Y %T')),
- reply_markup=BUTTONS)
+ await cb_wrapper.edit_message_text(
+ SUPPORT_REQUEST_SENT.format(queue=queue, date=time.strftime('%d/%m/%Y %T')),
+ reply_markup=BUTTONS)
join_chat_button = InlineKeyboardMarkup(
[[InlineKeyboardButton(JOIN_CHAT_BUTTON, f"join_{query.from_user.id}")]])
user = get_user(query.from_user.id)
tg_id, tg_uname, date, banned = user
- text = USER_INFO.format(tg_id=tg_id, tg_uname='@' + tg_uname if tg_uname else 'null', date=date,
+ text = USER_INFO.format(tg_id=tg_id,
+ tg_uname='@' + tg_uname if tg_uname else 'N/A',
+ date=date,
status=YES if banned else NO,
- admin='N/A')
+ admin=YES if tg_id in ADMINS else NO)
for admin in ADMINS:
status = CACHE[admin][0]
if status != "IN_CHAT":
@@ -150,7 +157,8 @@ async def close_chat(_, query):
await wrapper.send_message(query.from_user.id,
USER_LEAVES_CHAT)
await wrapper.send_message(CACHE[user_id][1],
- USER_CLOSES_CHAT.format(user_id=NAME.format(query.from_user.id), user_name=user_name))
+ USER_CLOSES_CHAT.format(user_id=NAME.format(query.from_user.id),
+ user_name=user_name))
del CACHE[query.from_user.id]
del CACHE[admin_id]
else:
@@ -179,7 +187,8 @@ async def forward_from_admin(_, message):
if CACHE[user_id][0] == "IN_CHAT":
del CACHE[user_id]
await wrapper.send_message(user_id,
- USER_CLOSES_CHAT.format(user_id=NAME.format(admin_id), user_name=admin_name))
+ USER_CLOSES_CHAT.format(user_id=NAME.format(admin_id),
+ user_name=admin_name))
del CACHE[message.from_user.id]
else:
await wrapper.send_message(CACHE[message.from_user.id][1],
@@ -187,12 +196,14 @@ async def forward_from_admin(_, message):
user_id=NAME.format(message.from_user.id),
message=message.text.html))
elif message.photo:
- await wrapper.send_photo(CACHE[message.from_user.id][1], photo=message.photo.file_id, file_ref=message.photo.file_ref,
+ await wrapper.send_photo(CACHE[message.from_user.id][1], photo=message.photo.file_id,
+ file_ref=message.photo.file_ref,
caption=ADMIN_MESSAGE.format(user_name=ADMINS[message.from_user.id],
user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else '' if message.caption else ''))
elif message.audio:
- await wrapper.send_audio(CACHE[message.from_user.id][1], audio=message.audio.file_id, file_ref=message.audio.file_ref,
+ await wrapper.send_audio(CACHE[message.from_user.id][1], audio=message.audio.file_id,
+ file_ref=message.audio.file_ref,
caption=ADMIN_MESSAGE.format(user_name=ADMINS[message.from_user.id],
user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
@@ -206,7 +217,8 @@ async def forward_from_admin(_, message):
await wrapper.send_sticker(CACHE[message.from_user.id][1], sticker=message.sticker.file_id,
file_ref=message.sticker.file_ref)
elif message.video:
- await wrapper.send_video(CACHE[message.from_user.id][1], video=message.video.file_id, file_ref=message.video.file_ref,
+ await wrapper.send_video(CACHE[message.from_user.id][1], video=message.video.file_id,
+ file_ref=message.video.file_ref,
caption=ADMIN_MESSAGE.format(user_name=ADMINS[message.from_user.id],
user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
@@ -217,7 +229,8 @@ async def forward_from_admin(_, message):
user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
elif message.voice:
- await wrapper.send_voice(CACHE[message.from_user.id][1], voice=message.voice.file_id, file_ref=message.voice.file_ref,
+ await wrapper.send_voice(CACHE[message.from_user.id][1], voice=message.voice.file_id,
+ file_ref=message.voice.file_ref,
caption=ADMIN_MESSAGE.format(user_name=ADMINS[message.from_user.id],
user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
@@ -250,32 +263,38 @@ async def forward_from_user(_, message):
USER_MESSAGE.format(user_name=name, user_id=NAME.format(message.from_user.id),
message=message.text.html))
elif message.photo:
- await wrapper.send_photo(CACHE[message.from_user.id][1], photo=message.photo.file_id, file_ref=message.photo.file_ref,
+ await wrapper.send_photo(CACHE[message.from_user.id][1], photo=message.photo.file_id,
+ file_ref=message.photo.file_ref,
caption=USER_MESSAGE.format(user_name=name, user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
elif message.audio:
- await wrapper.send_audio(CACHE[message.from_user.id][1], audio=message.audio.file_id, file_ref=message.audio.file_ref,
+ await wrapper.send_audio(CACHE[message.from_user.id][1], audio=message.audio.file_id,
+ file_ref=message.audio.file_ref,
caption=USER_MESSAGE.format(user_name=name, user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
elif message.document:
await wrapper.send_document(CACHE[message.from_user.id][1], document=message.document.file_id,
file_ref=message.document.file_ref,
- caption=USER_MESSAGE.format(user_name=name, user_id=NAME.format(message.from_user.id),
+ caption=USER_MESSAGE.format(user_name=name,
+ user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
elif message.sticker:
await wrapper.send_sticker(CACHE[message.from_user.id][1], sticker=message.sticker.file_id,
file_ref=message.sticker.file_ref)
elif message.video:
- await wrapper.send_video(CACHE[message.from_user.id][1], video=message.video.file_id, file_ref=message.video.file_ref,
+ await wrapper.send_video(CACHE[message.from_user.id][1], video=message.video.file_id,
+ file_ref=message.video.file_ref,
caption=USER_MESSAGE.format(user_name=name, user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
elif message.animation:
await wrapper.send_animation(CACHE[message.from_user.id][1], animation=message.animation.file_id,
file_ref=message.animation.file_ref,
- caption=USER_MESSAGE.format(user_name=name, user_id=NAME.format(message.from_user.id),
+ caption=USER_MESSAGE.format(user_name=name,
+ user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
elif message.voice:
- await wrapper.send_voice(CACHE[message.from_user.id][1], voice=message.voice.file_id, file_ref=message.voice.file_ref,
+ await wrapper.send_voice(CACHE[message.from_user.id][1], voice=message.voice.file_id,
+ file_ref=message.voice.file_ref,
caption=USER_MESSAGE.format(user_name=name, user_id=NAME.format(message.from_user.id),
message=message.caption.html or '' if message.caption else ''))
elif message.video_note:
@@ -323,8 +342,9 @@ async def join_chat(_, query):
for admin in ADMINS:
if CACHE[admin] != "IN_CHAT" and admin != admin_id:
await wrapper.send_message(admin,
- ADMIN_ACCEPTED_CHAT.format(admin=f"[{admin_name}]({NAME.format(admin)})",
- user=f"[{user_name}]({NAME.format(user_id)})"))
+ ADMIN_ACCEPTED_CHAT.format(
+ admin=f"[{admin_name}]({NAME.format(admin_id)})",
+ user=f"[{user_name}]({NAME.format(user_id)})"))
CACHE[user_id][-1].append((message.chat.id, message.message_id))
CACHE[user_id][-1].append((admin_joins.chat.id, admin_joins.message_id))
else:
diff --git a/BotBase/modules/start.py b/BotBase/modules/start.py
index 540b026..8e9da0e 100644
--- a/BotBase/modules/start.py
+++ b/BotBase/modules/start.py
@@ -1,5 +1,5 @@
"""
-Copyright 2020 Nocturn9x, alsoGAMER, CrisMystik
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -20,11 +20,12 @@
from pyrogram import Client, filters
from pyrogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message
-from BotBase.modules.antiflood import BANNED_USERS
-from BotBase.config import ADMINS, BACK_BUTTON, BUTTONS, CACHE, CREDITS, GREET, NAME, USER_LEFT_QUEUE, VERSION, bot, \
- user_banned
+from BotBase.config import ADMINS, BUTTONS, CACHE, CREDITS, GREET, NAME, VERSION, bot
+from BotBase.strings.default_strings import BACK_BUTTON, USER_LEFT_QUEUE
+from BotBase.methods.custom_filters import user_banned
from BotBase.database.query import get_users, set_user
from BotBase.methods import MethodWrapper
+from BotBase.modules.antiflood import BANNED_USERS
wrapper = MethodWrapper(bot)
@@ -86,8 +87,8 @@ async def cb_start_handler(_, message):
await start_handler(_, message)
-@Client.on_callback_query(filters.regex("bot_info") & ~BANNED_USERS)
-async def bot_info(_, query):
+@Client.on_callback_query(filters.regex("bot_about") & ~BANNED_USERS)
+async def bot_about(_, query):
cb_wrapper = MethodWrapper(query)
await cb_wrapper.edit_message_text(
text=CREDITS.format(VERSION=VERSION),
diff --git a/BotBase/strings/__init__.py b/BotBase/strings/__init__.py
new file mode 100644
index 0000000..fc2d656
--- /dev/null
+++ b/BotBase/strings/__init__.py
@@ -0,0 +1 @@
+__version__ = (2, 1, 0)
diff --git a/BotBase/strings/default_strings.py b/BotBase/strings/default_strings.py
new file mode 100644
index 0000000..6b9b7e4
--- /dev/null
+++ b/BotBase/strings/default_strings.py
@@ -0,0 +1,98 @@
+"""
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# region Flood Strings
+from BotBase.config import BAN_TIME
+
+FLOOD_NOTICE = f"š¤ Hey buddy!\nš Relax! You have been banned for {BAN_TIME / 60:.1f} minutes"
+# If you want the user to be notified of being flood-blocked, set this to the desired message, False to disable
+FLOOD_CLEARED = "ā»ļø Antiflood table cleaned up"
+FLOOD_USER_CLEARED = "ā»ļø Antiflood table for{user}
cleaned up"
+TOO_FAST = "ā Calm down! Try again later"
+# endregion
+
+# region Greet Buttons Strings
+SUPPORT_BUTTON = "š Support Chat"
+# The text for the button that triggers the live chat
+BACK_BUTTON = "š Back"
+# The text for the 'Back' button
+ABOUT_BUTTON = "ā¹ About"
+# The text for the 'About' button
+# endregion
+
+# region Livechat Strings
+MARKED_BUSY = "š² You're now busy, resend /busy to reset this state"
+UNMARKED_BUSY = "ā You'll now receive support requests again"
+CLOSE_CHAT_BUTTON = "ā Close chat"
+UPDATE_BUTTON = "š Update"
+ADMIN_ACCEPTED_CHAT = "ā
{admin} has joined the chat with {user}"
+USER_LEFT_QUEUE = "ā ļø {user} left the queue"
+QUEUE_LIST = "š» List of users waiting\n\n{queue}"
+CHATS_LIST = "š¬ List of users in chat\n\n{chats}"
+ADMIN_BUSY = "(Busy)"
+SUPPORT_NOTIFICATION = "š New support request!\n\n{uinfo}"
+ADMIN_JOINS_CHAT = " [{admin_name}]({admin_id}) joined the chat!"
+USER_CLOSES_CHAT = "š [{user_name}]({user_id}) closed the chat"
+USER_LEAVES_CHAT = "ā
You left the chat"
+USER_JOINS_CHAT = "ā
You've joined the chat"
+CHAT_BUSY = "ā ļø Another admin has already joined"
+LEAVE_CURRENT_CHAT = "ā ļø Close current chat first!"
+CANNOT_REQUEST_SUPPORT = "ā ļø You can't start a chat!"
+SUPPORT_REQUEST_SENT = "ā
You're now in the queue, wait for an admin to answer you\n\n" \
+ "š Admins available\n{queue}\nUpdated on: {date}
\n\nNote: " \
+ "If there are no admins available at the moment, press the 'Update' button every now and " \
+ "then to find out if a seat is available!"
+JOIN_CHAT_BUTTON = "ā Join the chat"
+# endregion
+
+# region User Interaction Strings
+USER_INFO_UPDATED = "ā
Information updated"
+USER_INFO_UNCHANGED = "ā I haven't detected any changes for this user"
+USER_BANNED = "ā
User banned successfully"
+USER_UNBANNED = "ā
User unbanned successfully"
+YOU_ARE_UNBANNED = "ā
You've been unbanned"
+USER_NOT_BANNED = "ā This user isn't banned"
+USER_ALREADY_BANNED = "ā This user is already banned"
+YOU_ARE_BANNED = "ā You've been banned"
+# endregion
+
+# region Whisper Strings
+WHISPER_FROM = "š£ Message from {admin}: {msg}"
+WHISPER_SUCCESSFUL = "ā
Sent successfully"
+# endregion
+
+# region Misc Strings
+YES = "Yes"
+NO = "No"
+# endregion
+
+# region Error Strings
+CANNOT_BAN_ADMIN = "ā Error: This user is an administrator"
+INVALID_SYNTAX = "ā Invalid syntax: Use {correct}
"
+# This is sent when a command is used the wrong way
+ERROR = "ā Error"
+# This is sent when a command returns an error
+NON_NUMERIC_ID = "The ID must be numeric!"
+# This is sent if the parameter to /getuser is not a numerical ID
+USERS_COUNT = "Total users: {count}
"
+# This is sent as a result of the /count command
+NO_PARAMETERS = "ā {command}
requires no parameters"
+# Error saying that the given command takes no parameters
+ID_MISSING = "The selected ID ({tg_id}
) isn't in the database"
+# Error when given ID is not in database
+NAME_MISSING = "The selected username ({tg_uname}
) isn't in the database"
+# Error when given username is not in database
+# endregion
diff --git a/DATABASE.md b/DATABASE.md
index b3d9c47..e8724be 100644
--- a/DATABASE.md
+++ b/DATABASE.md
@@ -1,14 +1,14 @@
# BotBase - Notes on database interaction
-BotBase has a built-in API to interact with an SQLite3 database, located in the
-`BotBase/database/query.py` module. The reason why SQLite3 was chosen among the
-lots of options is that it's lightweight, has fewer security concerns (no user
-and password to remember), and requires no setup at all.
+BotBase has a built-in API to interact with an MySQL database, located in the
+`BotBase/database/query.py` module. The reason why MySQL was chosen among the
+lots of options is that it's solid enough for bots with standard load,
+has enough features, and requires little to no setup at all.
The configuration is hassle-free, you can keep the default values, and they'll
work just fine. If you need a more complex database structure, just edit
-the `DB_CREATE` SQL query to fit your needs, but do not alter the default
-`users` table unless you also change all the SQL queries in the `config.py`
+the `CREATE_USERS_TABLE` SQL query to fit your needs, but do not alter the default
+`users` table unless you also change all the SQL queries in the `raw_queries.py`
file as this would otherwise break the whole internal machinery of BotBase.
## Available methods
@@ -17,10 +17,6 @@ The module `BotBase.database.query` implements the following default methods
to interact with the database. All methods either return the result of a query
or `True` if the operation was successful or an exception if the query errored.
-Please note that the methods are **NOT** locked and that proper locking is
-needed if you think that your bot might get a `sqlite3.OoerationalError: database
-is locked` error when accessing the database.
-
All queries are performed within a `with` block and therefore rollbacked
automatically if an error occurs or committed if the transaction was successful.
@@ -42,7 +38,7 @@ to the database. The username parameter can be `None`
- `update_user` -> Updates a user's username with the given ID
-# I need MySQL/other DBMS!
+# I need Sqlite/other DBMS!
The API has been designed in a way that makes it easy to swap between different
database managers, so if you feel in the right mood make a PR to support a new
@@ -53,7 +49,7 @@ database, and it'll be reviewed ASAP.
If you want to add custom methods to the API, we advise to follow the bot's convention:
-- Set the SQL query as a global variable whose name starts with `DB_` in `config.py`
+- Set the SQL query as a global variable whose name starts with `DB_` in `raw_queries.py`
- Import it in the `BotBase.database.query` module
- Create a new function that takes the required parameters whose name reflects the query name (without `DB_`)
- Perform the query in a `with` context manager, close the cursor when you're done
diff --git a/README.md b/README.md
index 9168d0d..6f2dcde 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ To set up a project using BotBase, follow this step-by-step guide (assuming `pip
- Open a terminal and type `git clone https://github.com/alsoGAMER/BotBase`
- `cd` into the newly created directory and run `python3 -m pip install -r requirements.txt`
- Once that is done, copy `BotBase/config.example.py` as `config.py` and edit your `config.py` module with a text editor and start changing the default settings
-- The first thing you might want to do is change the `API_ID`, `API_HASH`, and `BOT_TOKEN` global variables. Check [this page](https://my.telegram.org/apps) and log in with your Telegram account to create an `API_ID`/`API_HASH` pair by registering a new application. For the bot token, just create one with [BotFather](https://telegram.me/BotFather).
+- The first thing you might want to do is change the `API_ID`, `API_HASH`, and `BOT_TOKEN` global variables. Check [this page](https://my.telegram.org/apps) and log in with your Telegram account to create an `API_ID`/`API_HASH` pair by registering a new application. For the bot token, just create one with [BotFather](https://t.me/BotFather).
**Note**: The configuration file is still a python file, and when it will be imported any python code that you typed inside it will be executed, so be careful.
If you need to perform pre-startup operations it is advised to do them in the `if __name__ == "main":` block inside `bot.py`, before `bot.run()`
@@ -52,7 +52,7 @@ This plugin works the following way:
- Admins that are busy will not receive other support notifications
- An admin cannot join a chat if it's already busy
-Most of the working of the module is pre-defined, but you can customize the texts that the bot will use in the appropriate section of `config.py`
+Most of the working of the module is pre-defined, but you can customize the texts that the bot will use in the appropriate section of `strings/default_strings.py`
### Plugins - Admin
@@ -67,7 +67,7 @@ To configure this plugin, go to the appropriate section in `config.py` and chang
The available commands are:
-__Note__: ID can either be a Telegram User ID or a Telegram username (with or without the trailing @, case insensitive)
+__Note__: ID can either be a Telegram User ID or a Telegram username (with or without the trailing @, case-insensitive)
__Note__: Arguments marked with square brackets are optional
@@ -80,9 +80,9 @@ __Note__: Arguments marked with square brackets are optional
- `/whisper ID msg`: Send `msg` to a specific user. HTML and markdown formatting supported
- `/update ID`: Updates the user's info in the database, if they've changed
- `/busy`: Sets your admin status as busy/not busy to silence/unsilence support requests to you
-- `/clearflood [ID]` - Clears the antiflood local storage. If an user ID is given, only that user's cache is purged, otherwise the whole module-level cache is reset-
+- `/clearflood [ID]` - Clears the antiflood local storage. If a user ID is given, only that user's cache is purged, otherwise the whole module-level cache is reset-
-__Warning__: Altough acting on users by their username is supported, it is not recommended. Users can change their name and the bot wouldn't detect
+__Warning__: Altough acting on users by their username is supported, it is not recommended. Users can change their name, and the bot wouldn't detect
this change until you send the `/update` command. A telegram user can never change its ID (without deleting his own account) so that's way more reliable!
### Plugins - Antiflood
diff --git a/bot.py b/bot.py
index 5de7308..315e91d 100644
--- a/bot.py
+++ b/bot.py
@@ -1,5 +1,5 @@
"""
-Copyright 2020 Nocturn9x, alsoGAMER, CrisMystik
+Copyright 2020-2021 Nocturn9x, alsoGAMER, CrisMystik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -16,17 +16,18 @@
import logging
-from BotBase.config import bot, LOGGING_LEVEL, LOGGING_FORMAT, DATE_FORMAT, DB_PATH, DB_CREATE
-from BotBase.database.query import create_database
from pyrogram.session import Session
+from BotBase.config import bot, LOGGING_LEVEL, LOGGING_FORMAT, DATE_FORMAT
+from BotBase.database.raw_queries import CREATE_USERS_TABLE
+from BotBase.database.query import create_table
if __name__ == "__main__":
logging.basicConfig(format=LOGGING_FORMAT, datefmt=DATE_FORMAT, level=LOGGING_LEVEL)
Session.notice_displayed = True
try:
- logging.warning("Running create_database()")
- create_database(DB_PATH, DB_CREATE)
+ logging.warning("Running create_table()")
+ create_table(CREATE_USERS_TABLE)
logging.warning("Database interaction complete")
logging.warning("Starting bot")
bot.run()
diff --git a/requirements.txt b/requirements.txt
index 05e21e8..3e0fe6e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
-pyrogram==1.0.7
+pyrogram
tgcrypto
-wheel
\ No newline at end of file
+wheel
+mysqlclient
\ No newline at end of file