├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug-report---.md
│ ├── config.yml
│ └── feature-request---.md
└── workflows
│ └── stale.yml
├── .gitignore
├── LICENSE
├── README.md
├── assets
├── about.jpg
├── bmac.png
└── logo.png
├── config.py
├── main.py
├── requirements.txt
└── resources
├── lang_emojis.py
├── markups_handler.py
├── msg_handler.py
└── mysql_handler.py
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [fabston]
2 | liberapay: fabston
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report---.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Bug report \U0001F41E"
3 | about: Create a bug report
4 | labels: bug
5 |
6 | ---
7 |
8 | ## Describe the bug
9 | A clear and concise description of what the bug is.
10 |
11 | ### Steps to reproduce
12 | Steps to reproduce the behavior.
13 |
14 | ### Expected behavior
15 | A clear and concise description of what you expected to happen.
16 |
17 | ### Environment
18 | - OS: [e.g. Arch Linux]
19 | - Other details that you think may affect.
20 |
21 | ### Additional context
22 | Add any other context about the problem here.
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | contact_links:
2 | - name: Question 🙋
3 | url: https://github.com/fabston/Telegram-Support-Bot/discussions/categories/q-a
4 | about: Ask your question in our Discussions Channel
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request---.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Feature request \U0001F680"
3 | about: Suggest an idea
4 | labels: enhancement
5 |
6 | ---
7 |
8 | ## Summary
9 | Brief explanation of the feature.
10 |
11 | ### Basic example
12 | Include a basic example or links here.
13 |
14 | ### Motivation
15 | Why are we doing this? What use cases does it support? What is the expected outcome?
16 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: Mark and close stale issues and pull requests
2 |
3 | on:
4 | schedule:
5 | - cron: "0 0 * * *"
6 |
7 | jobs:
8 | stale:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/stale@v1
14 | with:
15 | repo-token: ${{ secrets.GITHUB_TOKEN }}
16 | stale-issue-message: 'It looks like this has been idle a while, so I am marking it as stale. Remove the label or comment if this issue should remain open.'
17 | stale-pr-message: 'It looks like this PR has been idle a while, so I am marking it as stale. Remove the label or comment if this is still being worked on.'
18 | stale-issue-label: 'no-activity'
19 | stale-pr-label: 'no-activity'
20 | exempt-pr-labels: 'help wanted', 'enhancement'
21 | days-before-stale: 30
22 | days-before-close: 5
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020-2021 fabston
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | About
15 | •
16 | Features
17 | •
18 | Installation
19 | •
20 | Images
21 | •
22 | Help
23 |
24 |
25 | ## About
26 | The **Telegram Support Bot** 📬 helps you to manage and organize your support inquiries.
27 |
28 | ## Features
29 | - **Text**, **Photos**, **Documents** and **Stickers** are being forwarded
30 | - Spam protection (sensitivity can be set in [`config.py`](https://github.com/fabston/Telegram-Support-Bot/blob/master/config.py))
31 | - Bad words filter (using regex, words can be set in [`config.py`](https://github.com/fabston/Telegram-Support-Bot/blob/master/config.py))
32 | - List all open/unanswered tickets (time passed since ticket opened is being shown as well)
33 | - Ban / Un-ban users (via reply or user id). User won't be able to interact with the bot anymore
34 | - List banned users, with last interaction point
35 | - Customisable FAQ text
36 | - Detect the users language and display it as an emoji
37 |
38 | > 💡 Got a feature idea? Open an [issue](https://github.com/fabston/Telegram-Support-Bot/issues/new?assignees=&labels=enhancement&template=feature-request---.md) and I might implement it.
39 |
40 | ### Staff commands
41 | | Command | Description |
42 | | --- | --- |
43 | | /ban | Ban user by ID or reply |
44 | | /unban | Un-ban user by ID or reply |
45 | | /banned | List banned users |
46 | | /tickets or /t | List open tickets |
47 | | /close or /c | Manually close a ticket by reply |
48 |
49 | ### User commands
50 | | Command | Description |
51 | | --- | --- |
52 | | /start | Starts the bot |
53 | | /faq | Show the FAQ's |
54 |
55 |
56 | ## Installation
57 | > ⚠️ Best to run the bot on a VPS. I can recommend Hetzner's CX11 VPS for 2.89€/month. [Sign up](https://hetzner.cloud/?ref=tQ1NdT8zbfNY) now and receive **€20 free** credits.
58 | 1. Log into MySQL (`sudo mysql`) and create a dedicated database and user with the following commands:
59 | 1. `CREATE DATABASE TelegramSupportBot;`
60 | 1. `CREATE USER 'SupportBotUser'@'localhost' IDENTIFIED BY '';`
61 | 1. `GRANT ALL PRIVILEGES ON TelegramSupportBot . * TO 'SupportBotUser'@'localhost';`
62 | 1. `exit;`
63 | 1. Clone this repository `git clone https://github.com/fabston/Telegram-Support-Bot.git`
64 | 1. Create your virtual environment `python3 -m venv Telegram-Support-Bot`
65 | 1. Activate it `source Telegram-Support-Bot/bin/activate && cd Telegram-Support-Bot`
66 | 1. Install all requirements `pip install -r requirements.txt`
67 | 1. Edit and update [`config.py`](https://github.com/fabston/Telegram-Support-Bot/blob/master/config.py)
68 | 1. Run the bot `python main.py`
69 |
70 |
71 | ## Images
72 | 
73 |
74 | ## How can I help?
75 | All kinds of contributions are welcome 🙌! The most basic way to show your support is to `⭐️ star` the project, or raise [`🐞 issues`](https://github.com/fabston/Telegram-Support-Bot/issues/new/choose).
76 |
77 | ***
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/assets/about.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fabston/Telegram-Support-Bot/1e46e01bd75198aa28960c345efa76487c11b27a/assets/about.jpg
--------------------------------------------------------------------------------
/assets/bmac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fabston/Telegram-Support-Bot/1e46e01bd75198aa28960c345efa76487c11b27a/assets/bmac.png
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fabston/Telegram-Support-Bot/1e46e01bd75198aa28960c345efa76487c11b27a/assets/logo.png
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------- #
2 | # Plugin Name : Telegram Support Bot #
3 | # Author Name : fabston #
4 | # File Name : config.py #
5 | # --------------------------------------------- #
6 |
7 | # Telegram
8 | token = '' # More: https://core.telegram.org/bots#3-how-do-i-create-a-bot
9 |
10 | # MySQL Database
11 | mysql_host = 'localhost'
12 | mysql_db = 'TelegramSupportBot'
13 | mysql_user = 'SupportBotUser'
14 | mysql_pw = ''
15 |
16 | # Support Chat (Chat ID)
17 | support_chat = # Example: -1001429781350 | To find out your channels ID use: https://t.me/getidsbot
18 |
19 | # Misc
20 | time_zone = 'GMT+2' # Supports time zone
21 | bad_words_toggle = True # Enable / disable bad words filter
22 | spam_toggle = True # Enable / disable spam filter
23 | spam_protection = 5 # How many consecutive messages can be sent without a reply from the team
24 | open_ticket_emoji = 24 # After X amount of HOURS an emoji will pop up at /tickets
25 |
26 | # Messages
27 | text_messages = {
28 | 'start': 'Hi {}, how can we help you today?',
29 | 'faqs': 'Your FAQ text goes in here.',
30 | 'support_response': 'From: {}' # Support response is being added automatically. {} = refers to the staffs first name.
31 | }
32 |
33 | # Regex (https://regex101.com/)
34 | regex_filter = {
35 | 'bad_words': r'(?i)^(.*?(\b\w*fuck|shut up|dick|bitch|bastart|cunt|bollocks|bugger|rubbish|wanker|twat|suck|ass|pussy|arsch\w*\b)[^$]*)$'
36 | }
37 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------- #
2 | # Plugin Name : Telegram Support Bot #
3 | # Author Name : fabston #
4 | # File Name : main.py #
5 | # --------------------------------------------- #
6 |
7 | import config
8 | from resources import mysql_handler as mysql
9 | from resources import markups_handler as markup
10 | from resources import msg_handler as msg
11 |
12 | import telebot
13 | from datetime import datetime
14 | import arrow
15 |
16 | bot = telebot.TeleBot(config.token)
17 |
18 | mysql.createTables
19 |
20 |
21 | # Callback Handlers
22 | @bot.callback_query_handler(func=lambda call: True)
23 | def callback_inline(call):
24 | if call.message:
25 | if call.data == "faqCallbackdata":
26 | bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
27 | text=config.text_messages['faqs'], parse_mode='Markdown',
28 | disable_web_page_preview=True)
29 |
30 |
31 | # Start Command
32 | @bot.message_handler(commands=['start'])
33 | def start(message):
34 | if message.chat.type == 'private':
35 | bot.send_message(message.chat.id,
36 | config.text_messages['start'].format(message.from_user.first_name) + msg.repo(),
37 | parse_mode='Markdown', disable_web_page_preview=True, reply_markup=markup.faqButton())
38 | mysql.start_bot(message.chat.id)
39 | else:
40 | bot.reply_to(message, 'Please send me a PM if you\'d like to talk to the Support Team.')
41 |
42 |
43 | # FAQ Command
44 | @bot.message_handler(commands=['faq'])
45 | def start(message):
46 | if message.chat.type == 'private':
47 | bot.reply_to(message, config.text_messages['faqs'], parse_mode='Markdown', disable_web_page_preview=True)
48 | else:
49 | pass
50 |
51 |
52 | # Get All Open Tickets
53 | @bot.message_handler(commands=['tickets', 't'])
54 | def ot_handler(message):
55 | if message.chat.id == config.support_chat:
56 | if not mysql.open_tickets:
57 | bot.reply_to(message, "ℹ️ Great job, you answered all your tickets!")
58 | return
59 |
60 | ot_msg = '📨 *Open tickets:*\n\n'
61 | for user in mysql.open_tickets:
62 | bot.send_chat_action(message.chat.id, 'typing')
63 | ot_link = mysql.user_tables(int(user))['open_ticket_link']
64 |
65 | now = arrow.now()
66 | diff = datetime.now() - mysql.user_tables(int(user))['open_ticket_time']
67 | diff.total_seconds() / 3600 # seconds to hour
68 | time_since_secs = float(diff.seconds)
69 | time_since = now.shift(seconds=-time_since_secs).humanize()
70 |
71 | # Bring attention to 1 day old tickets
72 | if time_since_secs > config.open_ticket_emoji * 3600:
73 | alert = ' ↳ ⚠️ '
74 | else:
75 | alert = ' ↳ '
76 |
77 | ot_msg += "• [{0}{1}](tg://user?id={2}) (`{2}`)\n{5}_{3}_ [➜ Go to msg]({4})\n".format(
78 | bot.get_chat(int(user)).first_name,
79 | ' {0}'.format(bot.get_chat(int(user)).last_name) if bot.get_chat(int(user)).last_name else '',
80 | int(user), time_since, ot_link, alert)
81 |
82 | bot.send_message(message.chat.id, ot_msg, parse_mode='Markdown')
83 | else:
84 | pass
85 |
86 |
87 | # Close a ticket manually
88 | @bot.message_handler(commands=['close', 'c'])
89 | def ot_handler(message):
90 | if message.chat.id == config.support_chat:
91 | if message.reply_to_message and '(#id' in message.reply_to_message.text:
92 | bot.send_chat_action(message.chat.id, 'typing')
93 | user_id = int(message.reply_to_message.text.split('(#id')[1].split(')')[0])
94 | ticket_status = mysql.user_tables(user_id)['open_ticket']
95 |
96 | if ticket_status == 0:
97 | bot.reply_to(message, '❌ That user has no open ticket...')
98 | else:
99 | # Reset Open Tickets as well as the Spamfilter
100 | mysql.reset_open_ticket(user_id)
101 | bot.reply_to(message, '✅ Ok, closed that users ticket!')
102 | else:
103 | bot.reply_to(message, 'ℹ️ You\'d have to reply to a message')
104 | else:
105 | pass
106 |
107 |
108 | # Get Banned User
109 | @bot.message_handler(commands=['banned'])
110 | def ot_handler(message):
111 | if message.chat.id == config.support_chat:
112 | if not mysql.banned:
113 | bot.reply_to(message, "ℹ️ Great news, nobody got banned... Yet.")
114 | return
115 |
116 | ot_msg = '⛔️ *Banned users:*\n\n'
117 | for user in mysql.banned:
118 | bot.send_chat_action(message.chat.id, 'typing')
119 | ot_link = mysql.user_tables(int(user))['open_ticket_link']
120 |
121 | ot_msg += "• [{0}{1}](tg://user?id={2}) (`{2}`)\n[➜ Go to last msg]({3})\n".format(
122 | bot.get_chat(int(user)).first_name,
123 | ' {0}'.format(bot.get_chat(int(user)).last_name) if bot.get_chat(int(user)).last_name else '',
124 | int(user), ot_link)
125 |
126 | bot.send_message(message.chat.id, ot_msg, parse_mode='Markdown')
127 | else:
128 | pass
129 |
130 |
131 | # Ban User
132 | @bot.message_handler(commands=['ban'])
133 | def ot_handler(message):
134 | try:
135 | if message.chat.id == config.support_chat:
136 | if message.reply_to_message and '(#id' in msg.msgCheck(message):
137 | user_id = msg.getUserID(message)
138 | banned_status = mysql.user_tables(user_id)['banned']
139 |
140 | if banned_status == 1:
141 | bot.reply_to(message, '❌ That user is already banned...')
142 | else:
143 | mysql.ban_user(user_id)
144 | try:
145 | # Reset Open Tickets as well as the Spamfilter
146 | mysql.reset_open_ticket(user_id)
147 | except Exception as e:
148 | pass
149 | bot.reply_to(message, '✅ Ok, banned that user!')
150 |
151 | elif msg.getReferrer(message.text):
152 | user_id = int(msg.getReferrer(message.text))
153 | banned_status = mysql.user_tables(user_id)['banned']
154 |
155 | if banned_status == 1:
156 | bot.reply_to(message, '❌ That user is already banned...')
157 | else:
158 | mysql.ban_user(user_id)
159 | try:
160 | # Reset Open Tickets as well as the Spamfilter
161 | mysql.reset_open_ticket(user_id)
162 | except Exception as e:
163 | pass
164 | bot.reply_to(message, '✅ Ok, banned that user!')
165 | else:
166 | bot.reply_to(message, 'ℹ️ You\'d have to either reply to a message or mention an `Users ID`.',
167 | parse_mode='Markdown')
168 | except TypeError:
169 | bot.reply_to(message, '❌ Are you sure I interacted with that user before...?')
170 |
171 |
172 | # Un-ban Useer
173 | @bot.message_handler(commands=['unban'])
174 | def ot_handler(message):
175 | try:
176 | if message.chat.id == config.support_chat:
177 | if message.reply_to_message and '(#id' in msg.msgCheck(message):
178 | user_id = msg.getUserID(message)
179 | banned_status = mysql.user_tables(user_id)['banned']
180 |
181 | if banned_status == 0:
182 | bot.reply_to(message, '❌ That user is already un-banned...')
183 | else:
184 | mysql.unban_user(user_id)
185 | bot.reply_to(message, '✅ Ok, un-banned that user!')
186 |
187 | elif msg.getReferrer(message.text):
188 | user_id = int(msg.getReferrer(message.text))
189 | banned_status = mysql.user_tables(user_id)['banned']
190 |
191 | if banned_status == 0:
192 | bot.reply_to(message, '❌ That user is already un-banned...')
193 | else:
194 | mysql.unban_user(user_id)
195 | bot.reply_to(message, '✅ Ok, un-banned that user!')
196 | else:
197 | bot.reply_to(message, 'ℹ️ You\'d have to either reply to a message or mention an `Users ID`.',
198 | parse_mode='Markdown')
199 | except TypeError:
200 | bot.reply_to(message, '❌ Are you sure I interacted with that user before...?')
201 |
202 |
203 | # Message Forward Handler (User - Support)
204 | @bot.message_handler(func=lambda message: message.chat.type == 'private', content_types=['text', 'photo', 'document'])
205 | def echo_all(message):
206 | while True:
207 | mysql.start_bot(message.chat.id)
208 | user_id = message.chat.id
209 | message = message
210 | banned = mysql.user_tables(user_id)['banned']
211 | ticket_status = mysql.user_tables(user_id)['open_ticket']
212 | ticket_spam = mysql.user_tables(user_id)['open_ticket_spam']
213 |
214 | if banned == 1:
215 | return
216 | elif msg.spam_handler_warning(bot, user_id, message): # First spam warning
217 | return
218 | elif msg.bad_words_handler(bot, message):
219 | return
220 | elif msg.spam_handler_blocked(bot, user_id, message): # Final spam warning // user cant send messages anymore
221 | return
222 | elif ticket_status == 0:
223 | mysql.open_ticket(user_id)
224 | continue
225 | else:
226 | msg.fwd_handler(user_id, bot, message)
227 | return
228 |
229 |
230 | # Message Forward Handler (Support - User)
231 | @bot.message_handler(content_types=['text', 'photo', 'document'])
232 | def echo_all(message):
233 | while True:
234 | try:
235 | try:
236 | user_id = msg.getUserID(message)
237 | message = message
238 | text = message.text
239 | msg_check = msg.msgCheck(message)
240 | ticket_status = mysql.user_tables(user_id)['open_ticket']
241 | banned_status = mysql.user_tables(user_id)['banned']
242 |
243 | if banned_status == 1:
244 | # If User is banned - un-ban user and sent message
245 | mysql.unban_user(user_id)
246 | bot.reply_to(message, 'ℹ️ *FYI: That user was banned.*\n_Un-banned and sent message!_',
247 | parse_mode='Markdown')
248 |
249 | elif ticket_status == 1:
250 | # Reset Open Tickets as well as the Spamfilter
251 | mysql.reset_open_ticket(user_id)
252 | continue
253 |
254 | else:
255 | if message.reply_to_message and '(#id' in msg_check:
256 | msg.snd_handler(user_id, bot, message, text)
257 | return
258 |
259 | except telebot.apihelper.ApiException:
260 | bot.reply_to(message, '❌ I was unable to send that message...\nThe user might\'ve blocked me.')
261 | return
262 |
263 | except Exception as e:
264 | bot.reply_to(message, '❌ Invalid command!')
265 | return
266 |
267 |
268 | print("Telegram Support Bot started...")
269 | bot.polling()
270 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pyTelegramBotAPI
2 | pymysql
3 | arrow
4 | cryptography
--------------------------------------------------------------------------------
/resources/lang_emojis.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------- #
2 | # Plugin Name : Telegram Support Bot #
3 | # Author Name : fabston #
4 | # File Name : lang_emojis.py #
5 | # --------------------------------------------- #
6 |
7 | def lang_emoji(lang):
8 | if lang == 'en':
9 | emoji = '🇺🇸'
10 | elif lang == 'de':
11 | emoji = '🇩🇪'
12 | elif lang == 'es':
13 | emoji = '🇪🇸'
14 | elif lang == 'se':
15 | emoji = '🇸🇪'
16 | elif lang == 'no':
17 | emoji = '🇳🇴'
18 | elif lang == 'ru':
19 | emoji = '🇷🇺'
20 | elif lang == 'ua':
21 | emoji = '🇺🇦'
22 | elif lang == 'it':
23 | emoji = '🇮🇹'
24 | elif lang == 'nz':
25 | emoji = '🇳🇿'
26 | elif lang == 'nl':
27 | emoji = '🇳🇱'
28 | elif lang == 'mx':
29 | emoji = '🇲🇽'
30 | elif lang == 'pt':
31 | emoji = '🇵🇹'
32 | elif lang == 'br':
33 | emoji = '🇧🇷'
34 | elif lang == 'au':
35 | emoji = '🇦🇺'
36 | elif lang == 'ca':
37 | emoji = '🇨🇦'
38 | elif lang == 'cr':
39 | emoji = '🇨🇷'
40 | elif lang == 'dk':
41 | emoji = '🇩🇰'
42 | elif lang == 'ie':
43 | emoji = '🇮🇪'
44 | elif lang == 'is':
45 | emoji = '🇮🇸'
46 | elif lang == 'th':
47 | emoji = '🇹🇭'
48 | elif lang == 'fr':
49 | emoji = '🇫🇷'
50 | elif lang == 'gr':
51 | emoji = '🇬🇷'
52 | elif lang == 'pl':
53 | emoji = '🇵🇱'
54 | elif lang == 'fi':
55 | emoji = '🇫🇮'
56 | elif lang == 'hk':
57 | emoji = '🇭🇰'
58 | elif lang == 'ar':
59 | emoji = '🇦🇷'
60 | elif lang == 'tr':
61 | emoji = '🇹🇷'
62 | elif lang == 'kr':
63 | emoji = '🇰🇷'
64 | elif lang == 'jp':
65 | emoji = '🇯🇵'
66 | elif lang == 'cn':
67 | emoji = '🇨🇳'
68 | elif lang == 'in':
69 | emoji = '🇮🇳'
70 | else:
71 | emoji = lang
72 |
73 | return emoji
--------------------------------------------------------------------------------
/resources/markups_handler.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------- #
2 | # Plugin Name : Telegram Support Bot #
3 | # Author Name : fabston #
4 | # File Name : markups_handler.py #
5 | # --------------------------------------------- #
6 |
7 | import config
8 | from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
9 |
10 |
11 | def faqButton():
12 | markup = InlineKeyboardMarkup()
13 | markup.add(InlineKeyboardButton('Read our FAQ\'s', callback_data='faqCallbackdata'))
14 | return markup
15 |
--------------------------------------------------------------------------------
/resources/msg_handler.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------- #
2 | # Plugin Name : Telegram Support Bot #
3 | # Author Name : fabston #
4 | # File Name : msg_handler.py #
5 | # --------------------------------------------- #
6 |
7 | import config
8 | from resources import mysql_handler as mysql
9 | from resources import lang_emojis as emoji
10 | import re
11 | import datetime
12 | import time
13 | import arrow
14 |
15 |
16 | def getReferrer(text):
17 | return text.split()[1] if len(text.split()) > 1 else None
18 |
19 |
20 | def msg_type(message):
21 | if message.content_type == 'text':
22 | msg_type = message.text
23 | elif message.content_type == 'photo' or message.content_type == 'document':
24 | msg_type = message.caption
25 | return msg_type
26 |
27 |
28 | def getUserID(message):
29 | if message.reply_to_message.content_type == 'text':
30 | user_id = int(message.reply_to_message.text.split('(#id')[1].split(')')[0])
31 | elif message.reply_to_message.content_type == 'photo' or message.reply_to_message.content_type == 'document':
32 | user_id = int(message.reply_to_message.caption.split('(#id')[1].split(')')[0])
33 | return user_id
34 |
35 |
36 | def msgCheck(message):
37 | if message.reply_to_message.content_type == 'text':
38 | msg_check = message.reply_to_message.text
39 | elif message.reply_to_message.content_type == 'photo' or message.reply_to_message.content_type == 'document':
40 | msg_check = message.reply_to_message.caption
41 | return msg_check
42 |
43 |
44 | def msgCaption(message):
45 | if message.caption == None:
46 | msgCaption = ''
47 | else:
48 | msgCaption = message.caption
49 | return msgCaption
50 |
51 |
52 | # (Support - User Handler)
53 | def snd_handler(user_id, bot, message, txt):
54 | try:
55 | if message.content_type == 'text':
56 | bot.send_chat_action(user_id, 'typing')
57 | bot.send_message(user_id, \
58 | config.text_messages['support_response'].format(
59 | bot.get_chat(user_id).first_name) + f'\n\n{message.text}', parse_mode='Markdown',
60 | disable_web_page_preview=True)
61 |
62 | elif message.content_type == 'photo':
63 | bot.send_chat_action(user_id, 'upload_photo')
64 | bot.send_photo(user_id, message.photo[-1].file_id,
65 | caption=config.text_messages['support_response'].format(
66 | bot.get_chat(user_id).first_name) + f'\n\n{msgCaption(message)}',
67 | parse_mode='Markdown')
68 |
69 | elif message.content_type == 'document':
70 | bot.send_chat_action(user_id, 'upload_document')
71 | bot.send_document(user_id, message.document.file_id,
72 | caption=config.text_messages['support_response'].format(
73 | bot.get_chat(user_id).first_name) + f'\n\n{msgCaption(message)}',
74 | parse_mode='Markdown')
75 | else:
76 | pass
77 |
78 | except Exception as e:
79 | print(e)
80 | bot.reply_to(message, '❌ That format is not supported.')
81 | return
82 |
83 |
84 | # (User - Support Handler)
85 | def fwd_handler(user_id, bot, message):
86 | # Update the Spamfilter
87 | mysql.spam(message.chat.id)
88 | lang_emoji = emoji.lang_emoji(message.from_user.language_code)
89 |
90 | if message.content_type == 'text':
91 | msg = bot.send_message(config.support_chat, "[{0}{1}](tg://user?id={2}) (#id{2}) | {3}\n\n{4}".format(
92 | message.from_user.first_name,
93 | ' {0}'.format(message.from_user.last_name) if message.from_user.last_name else '',
94 | message.from_user.id, lang_emoji, message.text), parse_mode='Markdown', disable_web_page_preview=True)
95 |
96 | elif message.content_type == 'photo':
97 | msg = bot.send_photo(config.support_chat, message.photo[-1].file_id,
98 | caption="[{0}{1}](tg://user?id={2}) (#id{2}) | {3}\n\n{4}".format(
99 | message.from_user.first_name,
100 | ' {0}'.format(message.from_user.last_name) if message.from_user.last_name else '',
101 | message.from_user.id, lang_emoji, msgCaption(message)), parse_mode='Markdown')
102 |
103 | elif message.content_type == 'document':
104 | msg = bot.send_document(config.support_chat, message.document.file_id,
105 | caption="[{0}{1}](tg://user?id={2}) (#id{2}) | {3}\n\n{4}".format(
106 | message.from_user.first_name,
107 | ' {0}'.format(message.from_user.last_name) if message.from_user.last_name else '',
108 | message.from_user.id, lang_emoji, msgCaption(message)), parse_mode='Markdown')
109 |
110 | elif message.content_type == 'sticker':
111 | msg = bot.send_sticker(user_id, message.sticker.file_id)
112 |
113 | else:
114 | bot.reply_to(message, '❌ That format is not supported and won\'t be forwarded.')
115 |
116 | channel_id = re.sub(r"-100(\S+)", r"\1", str(config.support_chat))
117 | message_id = msg.message_id
118 | message_link = f'https://t.me/c/{channel_id}/{message_id}'
119 | mysql.post_open_ticket(message_link, user_id)
120 |
121 | return fwd_handler
122 |
123 |
124 | def bad_words_handler(bot, message):
125 | if config.bad_words_toggle:
126 | try:
127 | if re.findall(config.regex_filter['bad_words'], msg_type(message)):
128 | bot.reply_to(message, '❗️ Watch your tongue...')
129 | return bad_words_handler
130 | except Exception as e:
131 | pass
132 |
133 |
134 | def time_zone():
135 | current_time = arrow.now(config.time_zone).strftime('%I:%M %p')
136 | return current_time
137 |
138 |
139 | def repo():
140 | msg = '\n\n[» Source Code](github.com/vsnz/Telegram-Support-Bot)'
141 | return msg
142 |
143 |
144 | def spam_handler_warning(bot, user_id, message):
145 | if config.spam_toggle:
146 | ticket_spam = mysql.user_tables(user_id)['open_ticket_spam']
147 | if ticket_spam > config.spam_protection:
148 | bot.reply_to(message,
149 | '{}, your messages are not being forwarded anymore. Please wait until the team responded. Thank you.\n\n' \
150 | f'_The support\'s local time is_ `{current_time}`.'.format(message.from_user.first_name),
151 | parse_mode='Markdown')
152 | return spam_handler_warning
153 |
154 |
155 | def spam_handler_blocked(bot, user_id, message):
156 | if config.spam_toggle:
157 | ticket_spam = mysql.user_tables(user_id)['open_ticket_spam']
158 | if ticket_spam == config.spam_protection - 1:
159 | fwd_handler(user_id, bot, message)
160 | bot.reply_to(message,
161 | 'We will be with you shortly.\n\n{}, to prevent spam you can only send us *1* more message.\n\n' \
162 | f'_The support\'s local time is_ `{current_time}`.'.format(message.from_user.first_name),
163 | parse_mode='Markdown')
164 | return spam_handler_blocked
165 |
166 |
167 | current_time = time_zone()
168 |
--------------------------------------------------------------------------------
/resources/mysql_handler.py:
--------------------------------------------------------------------------------
1 | # --------------------------------------------- #
2 | # Plugin Name : Telegram Support Bot #
3 | # Author Name : fabston #
4 | # File Name : mysql_handler.py #
5 | # --------------------------------------------- #
6 |
7 | import pymysql
8 | import config
9 | from datetime import datetime
10 |
11 |
12 | # Connect to MySQL Database
13 | def getConnection():
14 | connection = pymysql.connect(host=config.mysql_host,
15 | user=config.mysql_user,
16 | password=config.mysql_pw,
17 | db=config.mysql_db,
18 | charset='utf8mb4',
19 | cursorclass=pymysql.cursors.DictCursor,
20 | autocommit=True)
21 | return connection
22 |
23 |
24 | def createTables():
25 | connection = getConnection()
26 | with connection.cursor() as cursor:
27 | tablename = "users"
28 | try:
29 | cursor.execute(
30 | " CREATE TABLE `" + tablename + "` ( `userid` int(11) DEFAULT NULL, `open_ticket` int(4) DEFAULT 0, `banned` int(4) DEFAULT 0, \
31 | `open_ticket_spam` int(4) DEFAULT 1, `open_ticket_link` varchar(50) DEFAULT NULL, `open_ticket_time` datetime NOT NULL DEFAULT '1000-01-01 00:00:00')")
32 | return createTables
33 | except Exception as e:
34 | print(e)
35 |
36 |
37 | def spam(user_id):
38 | connection = getConnection()
39 | with connection.cursor() as cursor:
40 | sql = "SELECT banned, open_ticket, open_ticket_spam FROM users WHERE userid = %s"
41 | cursor.execute(sql, user_id)
42 | data = cursor.fetchone()
43 | ticket_spam = data['open_ticket_spam']
44 |
45 | sql = "UPDATE users SET open_ticket_spam = %s WHERE userid = %s"
46 | spam = ticket_spam + 1
47 | cursor.execute(sql, (spam, user_id))
48 | return spam
49 |
50 |
51 | def user_tables(user_id):
52 | connection = getConnection()
53 | with connection.cursor() as cursor:
54 | sql = "SELECT open_ticket, banned, open_ticket_time, open_ticket_spam, open_ticket_link FROM users WHERE userid = %s"
55 | cursor.execute(sql, user_id)
56 | data = cursor.fetchone()
57 | return data
58 |
59 |
60 | def getOpenTickets():
61 | connection = getConnection()
62 | with connection.cursor() as cursor:
63 | tmp = []
64 | cursor.execute("SELECT userid FROM users WHERE open_ticket = 1")
65 | for i in cursor.fetchall():
66 | tmp.append(i['userid'])
67 | return tmp
68 |
69 |
70 | def getBanned():
71 | connection = getConnection()
72 | with connection.cursor() as cursor:
73 | tmp = []
74 | cursor.execute("SELECT userid FROM users WHERE banned = 1")
75 | for i in cursor.fetchall():
76 | tmp.append(i['userid'])
77 | return tmp
78 |
79 |
80 | def start_bot(user_id):
81 | connection = getConnection()
82 | with connection.cursor() as cursor:
83 | sql = "SELECT EXISTS(SELECT userid FROM users WHERE userid = %s)"
84 | cursor.execute(sql, user_id)
85 | result = cursor.fetchone()
86 | # If the User never started the bot before, add the Telegram ID to the database
87 | if not list(result.values())[0]:
88 | sql = "INSERT INTO users(userid) VALUES (%s)"
89 | cursor.execute(sql, user_id)
90 |
91 |
92 | def open_ticket(user_id):
93 | connection = getConnection()
94 | with connection.cursor() as cursor:
95 | sql = "UPDATE users SET open_ticket = 1, open_ticket_time = %s WHERE userid = %s"
96 | now = datetime.now()
97 | cursor.execute(sql, (now, user_id))
98 | open_tickets.append(user_id)
99 | return open_ticket
100 |
101 |
102 | def post_open_ticket(link, msg_id):
103 | connection = getConnection()
104 | with connection.cursor() as cursor:
105 | sql = "UPDATE users SET open_ticket_link = %s WHERE userid = %s"
106 | cursor.execute(sql, (link, msg_id))
107 | return post_open_ticket
108 |
109 |
110 | def reset_open_ticket(user_id):
111 | connection = getConnection()
112 | with connection.cursor() as cursor:
113 | sql = "UPDATE users SET open_ticket = 0, open_ticket_spam = 1 WHERE userid = %s"
114 | cursor.execute(sql, user_id)
115 | open_tickets.pop(open_tickets.index(user_id))
116 | return reset_open_ticket
117 |
118 |
119 | def ban_user(user_id):
120 | connection = getConnection()
121 | with connection.cursor() as cursor:
122 | sql = "UPDATE users SET banned = 1 WHERE userid = %s"
123 | cursor.execute(sql, user_id)
124 | banned.append(user_id)
125 | return ban_user
126 |
127 |
128 | def unban_user(user_id):
129 | connection = getConnection()
130 | with connection.cursor() as cursor:
131 | sql = "UPDATE users SET banned = 0 WHERE userid = %s"
132 | cursor.execute(sql, user_id)
133 | banned.pop(banned.index(user_id))
134 | return unban_user
135 |
136 |
137 | createTables = createTables()
138 | open_tickets = getOpenTickets()
139 | banned = getBanned()
140 |
--------------------------------------------------------------------------------