├── runtime.txt
├── Procfile
├── .deepsource.toml
├── requirements.txt
├── LICENSE
├── acutebot
├── funcs
│ ├── __init__.py
│ ├── spotify.py
│ ├── favorite.py
│ ├── subtitles.py
│ ├── lyrics.py
│ ├── manga.py
│ ├── anime.py
│ ├── movies.py
│ ├── miscs.py
│ ├── tvseries.py
│ ├── music.py
│ └── inlinequery.py
├── helpers
│ ├── database
│ │ ├── __init__.py
│ │ ├── users_sql.py
│ │ ├── favorites_sql.py
│ │ └── spotify_sql.py
│ ├── parsedata.py
│ ├── keyboard.py
│ ├── spthelper.py
│ └── strings.py
├── __init__.py
└── __main__.py
├── README.md
├── app.py
└── .gitignore
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.8.5
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | worker: python3 -m acutebot
2 | web: python3 app.py
3 |
--------------------------------------------------------------------------------
/.deepsource.toml:
--------------------------------------------------------------------------------
1 | version = 1
2 |
3 | [[analyzers]]
4 | name = "python"
5 | enabled = true
6 |
7 | [analyzers.meta]
8 | runtime_version = "3.x.x"
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | python-telegram-bot==12.8
2 | pyrogram
3 | requests
4 | sqlalchemy
5 | psycopg2
6 | ujson
7 | tgcrypto
8 | pyfy
9 | lyricsgenius
10 | deezloader
11 | mutagen
12 | speedtest-cli>=2.0.2
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Stɑrry Shivɑm
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 |
--------------------------------------------------------------------------------
/acutebot/funcs/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 |
17 | from acutebot import LOG
18 | from os.path import dirname, basename, isfile
19 | import glob
20 |
21 | def list_modules():
22 | mod_paths = glob.glob(dirname(__file__) + "/*.py")
23 | all_modules = [basename(f)[:-3] for f in mod_paths if isfile(f)
24 | and f.endswith(".py")
25 | and not f.endswith('__init__.py')]
26 | return all_modules
27 |
28 | ALL_FUNCS = sorted(list_modules())
29 | LOG.info("Modules loaded: %s", str(ALL_FUNCS))
30 | __all__ = ALL_FUNCS + ["ALL_FUNCS"]
31 |
--------------------------------------------------------------------------------
/acutebot/helpers/database/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 |
17 | from acutebot import DB_URI
18 |
19 | from sqlalchemy import create_engine
20 | from sqlalchemy.ext.declarative import declarative_base
21 | from sqlalchemy.orm import sessionmaker, scoped_session
22 |
23 | def start() -> scoped_session:
24 | engine = create_engine(DB_URI, client_encoding="utf8")
25 | BASE.metadata.bind = engine
26 | BASE.metadata.create_all(engine)
27 | return scoped_session(sessionmaker(bind=engine, autoflush=False))
28 |
29 | BASE = declarative_base()
30 | SESSION = start()
31 |
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
αcutєвσt
3 |
4 | Modular telegram bot to get Movies, Anime, Muisc & much more!
5 |
6 |
7 |
8 | [](https://www.python.org/)
9 | [](https://opensource.org/licenses/MIT)
10 | [](http://makeapullrequest.com)
11 |
12 | Note: This is an old source code, this repository is not being updated anymore!
13 |
14 | Commands Available:
15 |
16 | /start
: Cool command to check if bot is working.
17 | /tvshows
: Gets you tv shows details.
18 | /movies
: Gets you movie details.
19 | /anime
: Gets your favorite anime details.
20 | /manga
: Gets you info about your favorite manga titles
21 | /lyrics
: Get lyrics of your favourite songs :)
22 | /music
: Download your favourite songs in FLAC & 320kbs quality!
23 | /nowplaying
: Flex your currently playing song in spotify.
24 | watchlist
: Get your saved watchlist :D
25 | subtitle
: Download subtitles for your favourite anime & movies
26 | /reddit
: Fun command to get memes scraped from reddit.
27 | /cancel
: Cancels the current ongoing task.
28 |
29 |
30 | Built with [Python telegram bot](https://github.com/python-telegram-bot/python-telegram-bot) library.
31 | This bot scrapes details of movies and TV shows, using [TheMovieDB](https://developers.themoviedb.org) API, Anime and
32 | Manga data from [Kitsu](https://kitsu.io/explore/anime), &
33 | Music stuffs from deezer using [deezloader](https://github.com/An0nimia/deezloader).
34 | Can be found in telegram as [𝙰𝚌𝚞𝚝𝚎𝙱𝚘𝚝](https://t.me/acutebot)!
35 |
36 |
37 | Contributions of all sizes are welcome :)
38 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import os, tornado
17 | from telegram import Bot
18 | from acutebot import TOKEN
19 | from acutebot.helpers.database.spotify_sql import update_creds
20 | from acutebot.helpers.spthelper import SpotifyClient
21 |
22 |
23 | bot = Bot(TOKEN)
24 |
25 |
26 | class MainHandler(tornado.web.RequestHandler):
27 | def get(self):
28 | self.write("Hello World!")
29 |
30 |
31 | class SpotifyCallback(tornado.web.RequestHandler):
32 | def get(self):
33 | if self.get_argument("code", ""):
34 | grant = self.get_argument("code", "")
35 | callback_state = self.get_argument("state", "")
36 | spotify = SpotifyClient()
37 | user_creds = spotify.build_user_creds(grant=grant)
38 | update_creds(
39 | callback_state,
40 | user_creds.id,
41 | user_creds.access_token,
42 | user_creds.refresh_token,
43 | )
44 | print("user logged in successfully")
45 | bot.sendMessage(callback_state, "Successfully logged in!")
46 | self.redirect("https://t.me/" + bot.username)
47 |
48 |
49 | urls = [(r"/", MainHandler), (r"/acutebot/webserver", SpotifyCallback)]
50 | PORT = os.environ.get("PORT", 8888)
51 |
52 |
53 | def start():
54 |
55 | print("Tornado server started")
56 |
57 | app = tornado.web.Application(urls)
58 | app.listen(PORT, address="0.0.0.0")
59 | tornado.ioloop.IOLoop.current().start()
60 |
61 |
62 | if __name__ == "__main__":
63 | start()
64 |
--------------------------------------------------------------------------------
/acutebot/helpers/database/users_sql.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import threading
17 |
18 | from sqlalchemy import Column, Integer, UnicodeText
19 | from acutebot.helpers.database import SESSION, BASE
20 | from acutebot import dp
21 |
22 |
23 | class Users(BASE):
24 | __tablename__ = "users"
25 | user_id = Column(Integer, primary_key=True)
26 | username = Column(UnicodeText)
27 |
28 | def __init__(self, user_id, username=None):
29 | self.user_id = user_id
30 | self.username = username
31 |
32 | def __repr__(self):
33 | return "".format(self.username, self.user_id)
34 |
35 |
36 | Users.__table__.create(checkfirst=True)
37 | INSERTION_LOCK = threading.RLock()
38 |
39 |
40 | def ensure_bot_in_db():
41 | with INSERTION_LOCK:
42 | bot = Users(dp.bot.id, dp.bot.username)
43 | SESSION.merge(bot)
44 | SESSION.commit()
45 |
46 |
47 | def update_user(user_id, username):
48 | with INSERTION_LOCK:
49 | user = SESSION.query(Users).get(user_id)
50 | if not user:
51 | user = Users(user_id, username)
52 | SESSION.add(user)
53 | SESSION.flush()
54 | else:
55 | user.username = username
56 | SESSION.commit()
57 |
58 |
59 | def get_all_users():
60 | try:
61 | return SESSION.query(Users).all()
62 | finally:
63 | SESSION.close()
64 |
65 |
66 | def users_count():
67 | try:
68 | return SESSION.query(Users).count()
69 | finally:
70 | SESSION.close()
71 |
72 | ensure_bot_in_db()
73 |
--------------------------------------------------------------------------------
/acutebot/helpers/database/favorites_sql.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import threading
17 | from acutebot.helpers.database import SESSION, BASE
18 | from sqlalchemy import Column, UnicodeText, Numeric
19 |
20 |
21 | class Favourites(BASE):
22 | __tablename__ = "favourites"
23 | user_id = Column(Numeric, primary_key=True)
24 | data = Column(UnicodeText, primary_key=True)
25 |
26 | def __init__(self, user_id, data):
27 | self.user_id = user_id
28 | self.data = data
29 |
30 |
31 | Favourites.__table__.create(checkfirst=True)
32 | FAV_INSERTION_LOCK = threading.RLock()
33 |
34 |
35 | def check_fav(user_id, data):
36 | try:
37 | return SESSION.query(Favourites).get((int(user_id), str(data)))
38 | finally:
39 | SESSION.close()
40 |
41 |
42 | def get_fav(user_id):
43 | try:
44 | return (
45 | SESSION.query(Favourites).filter(Favourites.user_id == int(user_id)).all()
46 | )
47 | finally:
48 | SESSION.close()
49 |
50 |
51 | def add_fav(user_id, data):
52 | with FAV_INSERTION_LOCK:
53 | to_check = check_fav(user_id, data)
54 | if not to_check:
55 | adder = Favourites(int(user_id), str(data))
56 | SESSION.add(adder)
57 | SESSION.commit()
58 | return True
59 | return False
60 |
61 |
62 | def remove_fav(user_id):
63 | with FAV_INSERTION_LOCK:
64 | to_check = get_fav(user_id)
65 | if not to_check:
66 | return False
67 | rem = SESSION.query(Favourites).filter(Favourites.user_id == user_id)
68 | rem.delete()
69 | SESSION.commit()
70 | return True
71 |
72 |
73 | def fav_count():
74 | try:
75 | return SESSION.query(Favourites).count()
76 | finally:
77 | SESSION.close()
78 |
--------------------------------------------------------------------------------
/acutebot/helpers/database/spotify_sql.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE
14 |
15 |
16 | import threading
17 |
18 | from sqlalchemy import Column, Integer, UnicodeText
19 | from acutebot.helpers.database import SESSION, BASE
20 |
21 |
22 | class SpotifyCreds(BASE):
23 | __tablename__ = "spotifycreds"
24 | user_id = Column(Integer, primary_key=True)
25 | spotify_id = Column(UnicodeText)
26 | spotify_access_token = Column(UnicodeText)
27 | spotify_refresh_token = Column(UnicodeText)
28 |
29 | def __init__(
30 | self,
31 | user_id,
32 | spotify_id=None,
33 | spotify_access_token=None,
34 | spotify_refresh_token=None,
35 | ):
36 | self.user_id = user_id
37 | self.spotify_id = spotify_id
38 | self.spotify_access_token = spotify_access_token
39 | self.spotify_refresh_token = spotify_refresh_token
40 |
41 |
42 | SpotifyCreds.__table__.create(checkfirst=True)
43 | SPT_INSERTION_LOCK = threading.RLock()
44 |
45 |
46 | def update_creds(
47 | user_id, spotify_id=None, spotify_access_token=None, spotify_refresh_token=None
48 | ):
49 | with SPT_INSERTION_LOCK:
50 | sptcreds = SESSION.query(SpotifyCreds).get(user_id)
51 | if not sptcreds:
52 | sptcreds = SpotifyCreds(
53 | user_id, spotify_id, spotify_access_token, spotify_refresh_token
54 | )
55 | SESSION.add(sptcreds)
56 | SESSION.flush()
57 | else:
58 | sptcreds.spotify_id = spotify_id
59 | sptcreds.spotify_access_token = spotify_access_token
60 | sptcreds.spotify_refresh_token = spotify_refresh_token
61 | SESSION.commit()
62 |
63 |
64 | def get_sptuser(user_id):
65 | try:
66 | return SESSION.query(SpotifyCreds).get(user_id)
67 | finally:
68 | SESSION.close()
69 |
--------------------------------------------------------------------------------
/acutebot/funcs/spotify.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | from telegram.ext import CommandHandler
17 | from telegram.ext.dispatcher import run_async
18 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton
19 |
20 | from acutebot import dp, typing
21 | from acutebot.helpers.spthelper import SpotifyClient, get_spotify_data
22 | import acutebot.helpers.strings as st
23 |
24 |
25 | def authorize(update, user_id):
26 | msg = update.effective_message
27 | user = update.effective_user
28 | spotify = SpotifyClient()
29 | if spotify.is_oauth_ready:
30 | url = spotify.auth_uri(state=user_id)
31 | msg.reply_text(
32 | st.SPT_LOGIN.format(user.first_name),
33 | reply_markup=InlineKeyboardMarkup(
34 | [[InlineKeyboardButton(text="Authorize", url=url)]]
35 | ),
36 | )
37 |
38 | else:
39 | msg.reply_text("Something went wrong! Please report to @starryboi")
40 |
41 |
42 | @run_async
43 | @typing
44 | def now_playing(update, context):
45 | user = update.effective_user
46 | chat = update.effective_chat
47 | msg = update.effective_message
48 |
49 | spt = get_spotify_data(user.id)
50 | if not spt:
51 | if chat.type == "private":
52 | return authorize(update, user.id)
53 | return msg.reply_text(st.SPT_LOGIN_PM)
54 |
55 | text = ""
56 | music = spt.current_music
57 | if music:
58 | text = f"{user.first_name} is currently listening to"
59 | else:
60 | music = spt.last_music
61 | text = f"{user.first_name} was listening to"
62 |
63 | text += f" {music.name} by {music.artist}"
64 | msg.reply_text(
65 | text,
66 | reply_markup=InlineKeyboardMarkup(
67 | [[InlineKeyboardButton(text="Open in spotify", url=music.url)]]
68 | ),
69 | )
70 |
71 |
72 | dp.add_handler(CommandHandler("nowplaying", now_playing))
73 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #Config and db logfile
2 | acutebot/config.py
3 | acute.session
4 | logfile
5 | acute-lyrics.txt
6 | temp/
7 |
8 | # Byte-compiled / optimized / DLL files
9 | __pycache__/
10 | *.py[cod]
11 | *$py.class
12 |
13 | # C extensions
14 | *.so
15 |
16 | # Distribution / packaging
17 | .Python
18 | build/
19 | develop-eggs/
20 | dist/
21 | downloads/
22 | eggs/
23 | .eggs/
24 | lib/
25 | lib64/
26 | parts/
27 | sdist/
28 | var/
29 | wheels/
30 | pip-wheel-metadata/
31 | share/python-wheels/
32 | *.egg-info/
33 | .installed.cfg
34 | *.egg
35 | MANIFEST
36 |
37 | # PyInstaller
38 | # Usually these files are written by a python script from a template
39 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
40 | *.manifest
41 | *.spec
42 |
43 | # Installer logs
44 | pip-log.txt
45 | pip-delete-this-directory.txt
46 |
47 | # Unit test / coverage reports
48 | htmlcov/
49 | .tox/
50 | .nox/
51 | .coverage
52 | .coverage.*
53 | .cache
54 | nosetests.xml
55 | coverage.xml
56 | *.cover
57 | *.py,cover
58 | .hypothesis/
59 | .pytest_cache/
60 |
61 | # Translations
62 | *.mo
63 | *.pot
64 |
65 | # Django stuff:
66 | *.log
67 | local_settings.py
68 | db.sqlite3
69 | db.sqlite3-journal
70 |
71 | # Flask stuff:
72 | instance/
73 | .webassets-cache
74 |
75 | # Scrapy stuff:
76 | .scrapy
77 |
78 | # Sphinx documentation
79 | docs/_build/
80 |
81 | # PyBuilder
82 | target/
83 |
84 | # Jupyter Notebook
85 | .ipynb_checkpoints
86 |
87 | # IPython
88 | profile_default/
89 | ipython_config.py
90 |
91 | # pyenv
92 | .python-version
93 |
94 | # pipenv
95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
98 | # install all needed dependencies.
99 | #Pipfile.lock
100 |
101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
102 | __pypackages__/
103 |
104 | # Celery stuff
105 | celerybeat-schedule
106 | celerybeat.pid
107 |
108 | # SageMath parsed files
109 | *.sage.py
110 |
111 | # Environments
112 | .env
113 | .venv
114 | env/
115 | venv/
116 | ENV/
117 | env.bak/
118 | venv.bak/
119 |
120 | # Spyder project settings
121 | .spyderproject
122 | .spyproject
123 |
124 | # Rope project settings
125 | .ropeproject
126 |
127 | # mkdocs documentation
128 | /site
129 |
130 | # mypy
131 | .mypy_cache/
132 | .dmypy.json
133 | dmypy.json
134 |
135 | # Pyre type checker
136 | .pyre/
137 |
--------------------------------------------------------------------------------
/acutebot/funcs/favorite.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | from telegram.ext import CommandHandler, CallbackQueryHandler
17 | from telegram.ext.dispatcher import run_async
18 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton
19 |
20 | from acutebot import dp, typing
21 | import acutebot.helpers.strings as st
22 | import acutebot.helpers.database.favorites_sql as sql
23 |
24 |
25 | @run_async
26 | def add_favorite(update, context):
27 | query = update.callback_query
28 | userid = update.effective_user.id
29 | match = query.data.split("_")[1]
30 |
31 | add = sql.add_fav(userid, match)
32 | if add:
33 | query.answer(st.SAVED_FAV.format(match), show_alert=True)
34 | else:
35 | query.answer(st.FAV_EXIST, show_alert=True)
36 |
37 |
38 | @run_async
39 | @typing
40 | def list_favorite(update, context):
41 | msg = update.effective_message
42 | user = update.effective_user
43 | fav = sql.get_fav(user.id)
44 | if fav:
45 | text = "🔖 Your watchlist:\n\n"
46 | for title in fav:
47 | text += f"• {title.data}\n"
48 | keyb = [
49 | [InlineKeyboardButton(text="Watched ✅", callback_data=f"remfav_{user.id}")]
50 | ]
51 | msg.reply_text(text, reply_markup=InlineKeyboardMarkup(keyb))
52 | else:
53 | msg.reply_text(st.NOFAVS)
54 |
55 |
56 | @run_async
57 | def rem_favorite(update, context):
58 | query = update.callback_query
59 | user = update.effective_user
60 | user_id = query.data.split("_")[1]
61 |
62 | if user.id == int(user_id):
63 | sql.remove_fav(user_id)
64 | query.message.edit_text(st.REMFAV)
65 | else:
66 | query.answer(st.NOT_ALLOWED, show_alert=True)
67 |
68 |
69 | LIST_FAV_HANDLER = CommandHandler("watchlist", list_favorite)
70 | FAV_CLEAR_HANDLER = CallbackQueryHandler(rem_favorite, pattern=r"remfav_")
71 | FAV_ADD_HANDLER = CallbackQueryHandler(add_favorite, pattern=r"addfav_")
72 |
73 |
74 | dp.add_handler(LIST_FAV_HANDLER)
75 | dp.add_handler(FAV_CLEAR_HANDLER)
76 | dp.add_handler(FAV_ADD_HANDLER)
77 |
--------------------------------------------------------------------------------
/acutebot/helpers/parsedata.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import itertools
17 | from uuid import uuid4
18 | from telegram.constants import MAX_CAPTION_LENGTH as MAX_CAP_LEN
19 | from telegram import InlineQueryResultArticle, InputTextMessageContent
20 |
21 |
22 | def byname(val):
23 | if val == "":
24 | return "Not available"
25 | datalist = []
26 | for x in val:
27 | datalist.append(x["name"])
28 | data = ", ".join(datalist)
29 | return data
30 |
31 |
32 | def currency(val):
33 | """Format currency"""
34 | if val == "":
35 | return "Not available"
36 | curr = "${:,.2f}".format(val)
37 | return curr
38 |
39 |
40 | def byindex(val):
41 | try:
42 | return val[0]["name"]
43 | except IndexError:
44 | return "Not Available"
45 |
46 |
47 | def tvruntime(val):
48 | try:
49 | return str(val[0]) + " minutes"
50 | except IndexError:
51 | return "Not Available"
52 |
53 |
54 | def article(
55 | title="", description="", message_text="", thumb_url=None, reply_markup=None
56 | ):
57 | return InlineQueryResultArticle(
58 | id=uuid4(),
59 | title=title,
60 | description=description,
61 | thumb_url=thumb_url,
62 | input_message_content=InputTextMessageContent(
63 | message_text=message_text, disable_web_page_preview=False
64 | ),
65 | reply_markup=reply_markup,
66 | )
67 |
68 |
69 | def paginate(iterable, page_size):
70 | while True:
71 | i1, i2 = itertools.tee(iterable)
72 | iterable, page = (
73 | itertools.islice(i1, page_size, None),
74 | list(itertools.islice(i2, page_size)),
75 | )
76 | if len(page) == 0:
77 | break
78 | yield list(page)
79 |
80 |
81 | def sort_caps(text, c_id, tv=False, mv=False, anime=False, manga=False):
82 | if len(text) > MAX_CAP_LEN:
83 | text = text[: MAX_CAP_LEN - 80]
84 | text += ""
85 | if tv:
86 | text += f"...read more"
87 | if mv:
88 | text += (
89 | f"...read more"
90 | )
91 | if anime:
92 | text += f"...read more"
93 | if manga:
94 | text += f"...read more"
95 |
96 | return text
97 |
--------------------------------------------------------------------------------
/acutebot/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import os, sys, logging
17 | from functools import wraps
18 | from telegram.ext import Updater, Defaults
19 | from telegram import ChatAction, ParseMode
20 |
21 | ENV = bool(os.environ.get("ENV", False))
22 | if ENV:
23 | TOKEN = os.environ.get("TOKEN")
24 | WORKERS = int(os.environ.get("WORKERS", 8))
25 | TMDBAPI = os.environ.get("TMDBAPI")
26 | DB_URI = os.environ.get("DATABASE_URL")
27 | GENIUS = os.environ.get("GENIUS")
28 | SPT_CLIENT_SECRET = os.environ.get("SPT_CLIENT_SECRET")
29 | SPT_CLIENT_ID = os.environ.get("SPT_CLIENT_ID")
30 | DEBUG = bool(os.environ.get("DEBUG", False))
31 | ARLTOKEN = os.environ.get("ARL")
32 | APP_URL = os.environ.get("APP_URL")
33 | APIID = os.environ.get("APIID")
34 | APIHASH = os.environ.get("APIHASH")
35 |
36 |
37 | else:
38 | from acutebot.config import Config
39 |
40 | TOKEN = Config.TOKEN
41 | WORKERS = Config.WORKERS
42 | TMDBAPI = Config.TMDBAPI
43 | DB_URI = Config.DB_URI
44 | GENIUS = Config.GENIUS
45 | SPT_CLIENT_SECRET = Config.SPT_CLIENT_SECRET
46 | SPT_CLIENT_ID = Config.SPT_CLIENT_ID
47 | DEBUG = Config.DEBUG
48 | ARLTOKEN = Config.ARL
49 | APP_URL = Config.APP_URL
50 | APIID = Config.APIID
51 | APIHASH = Config.APIHASH
52 |
53 |
54 | if DEBUG:
55 | logging.basicConfig(
56 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
57 | level=logging.DEBUG,
58 | )
59 | else:
60 | logging.basicConfig(
61 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
62 | level=logging.INFO,
63 | )
64 |
65 | __version__ = "1.1.3-rev09"
66 |
67 | DEV_ID = 894380120
68 | LOG = logging.getLogger(__name__)
69 |
70 | # Check python version:
71 | if sys.version_info[0] < 3 or sys.version_info[1] < 6:
72 | LOG.info("You MUST need to have python version 3.6! shutting down...")
73 | sys.exit(1)
74 |
75 |
76 | def typing(func):
77 | """Sends typing action while processing func command."""
78 |
79 | @wraps(func)
80 | def command_func(update, context, *args, **kwargs):
81 | context.bot.send_chat_action(
82 | chat_id=update.effective_chat.id, action=ChatAction.TYPING
83 | )
84 | return func(update, context, *args, **kwargs)
85 |
86 | return command_func
87 |
88 |
89 | # Use HTML treewide;
90 | defaults = Defaults(parse_mode=ParseMode.HTML)
91 | updater = Updater(TOKEN, use_context=True, workers=WORKERS, defaults=defaults)
92 | dp = updater.dispatcher
93 |
--------------------------------------------------------------------------------
/acutebot/helpers/keyboard.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | from telegram import InlineKeyboardButton
17 |
18 |
19 | def keyboard(
20 | ytkey=None,
21 | homepage=None,
22 | title=None,
23 | imdbid=None,
24 | tv_id=None,
25 | mv_id=None,
26 | anime_ytkey=None,
27 | anime_id=None,
28 | manga_id=None,
29 | ):
30 | """
31 | Attach InlineKeyboardButton dynamically from data
32 | """
33 |
34 | keyblist = [[]]
35 | if ytkey:
36 | if len(ytkey["results"]) > 0:
37 | ytkey = ytkey["results"][0]["key"]
38 | keyblist[0].append(
39 | InlineKeyboardButton(
40 | text="📹 Trailer", url=f"https://www.youtube.com/watch?v={ytkey}"
41 | )
42 | )
43 |
44 | if homepage and homepage != "":
45 | keyblist.append([InlineKeyboardButton(text="📃 Homepage", url=homepage)])
46 |
47 | if imdbid:
48 | keyblist[0].append(
49 | InlineKeyboardButton(
50 | text="🎞️ IMDb", url=f"https://m.imdb.com/title/{imdbid}"
51 | )
52 | )
53 |
54 | if title:
55 | keyblist.append(
56 | [
57 | InlineKeyboardButton(
58 | text="Save to watchlist 🔖", callback_data=f"addfav_{title[:54]}"
59 | )
60 | ]
61 | )
62 |
63 | if tv_id:
64 | keyblist.append(
65 | [
66 | InlineKeyboardButton(
67 | text="More information",
68 | url=f"https://www.themoviedb.org/tv/{tv_id}",
69 | )
70 | ]
71 | )
72 |
73 | if mv_id:
74 | keyblist.append(
75 | [
76 | InlineKeyboardButton(
77 | text="More information",
78 | url=f"https://www.themoviedb.org/movie/{mv_id}",
79 | )
80 | ]
81 | )
82 |
83 | if anime_ytkey:
84 | keyblist[0].append(
85 | InlineKeyboardButton(
86 | text="📹 Trailer", url=f"https://www.youtube.com/watch?v={anime_ytkey}"
87 | )
88 | )
89 |
90 | if anime_id:
91 | keyblist[0].append(
92 | InlineKeyboardButton(
93 | text="Information", url=f"https://kitsu.io/anime/{anime_id}"
94 | )
95 | )
96 |
97 | if manga_id:
98 | keyblist[0].append(
99 | InlineKeyboardButton(
100 | text="More Information", url=f"https://kitsu.io/manga/{manga_id}"
101 | )
102 | )
103 | return keyblist
104 |
--------------------------------------------------------------------------------
/acutebot/helpers/spthelper.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | from pyfy import Spotify as Pyfy, ClientCreds, UserCreds
17 | from pyfy.excs import ApiError
18 | from dataclasses import dataclass
19 |
20 | from acutebot import TOKEN, SPT_CLIENT_SECRET, SPT_CLIENT_ID, APP_URL
21 | from acutebot.helpers.database.spotify_sql import get_sptuser
22 |
23 | import typing
24 |
25 |
26 | @dataclass
27 | class Music:
28 | id: str
29 | name: str
30 | artist: str
31 | url: str
32 | thumbnail: str
33 |
34 | def __init__(self, music: dict):
35 | self.id = music["id"]
36 | self.name = music["name"]
37 | self.artist = music["artists"][0]["name"]
38 | self.url = music["external_urls"]["spotify"]
39 | self.thumbnail = music["album"]["images"][1]["url"]
40 |
41 |
42 | class Spotify:
43 | def __init__(self, user):
44 |
45 | try:
46 | self._client = SpotifyClient(
47 | access_token=user.spotify_access_token,
48 | refresh_token=user.spotify_refresh_token,
49 | )
50 | except ApiError:
51 | raise Exception("tokens invalid")
52 |
53 | @property
54 | def current_music(self) -> typing.Optional[Music]:
55 | try:
56 | current_status = self._client.currently_playing()
57 | music = current_status["item"]
58 | return Music(music)
59 | except Exception:
60 | return
61 |
62 | @property
63 | def last_music(self) -> Music:
64 | music = self._client.recently_played_tracks(limit=1)["items"][0]["track"]
65 | return Music(music)
66 |
67 |
68 | class SpotifyClient(Pyfy):
69 | def __init__(
70 | self, access_token=None, refresh_token=None,
71 | ):
72 | scopes = [
73 | "user-read-recently-played",
74 | "user-read-playback-state",
75 | ]
76 |
77 | user_creds = None
78 |
79 | if access_token and refresh_token:
80 | user_creds = UserCreds(
81 | access_token=access_token, refresh_token=refresh_token
82 | )
83 |
84 | super().__init__(
85 | client_creds=ClientCreds(
86 | client_id=SPT_CLIENT_ID,
87 | client_secret=SPT_CLIENT_SECRET,
88 | redirect_uri=APP_URL + "acutebot/webserver",
89 | scopes=scopes,
90 | ),
91 | user_creds=user_creds,
92 | )
93 |
94 |
95 | def get_spotify_data(user_id):
96 | try:
97 | user = get_sptuser(user_id)
98 | return Spotify(user)
99 | except Exception:
100 | return False
101 |
--------------------------------------------------------------------------------
/acutebot/funcs/subtitles.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import requests as r
17 | from io import BytesIO
18 |
19 | from telegram.ext import (
20 | MessageHandler,
21 | CommandHandler,
22 | CallbackQueryHandler,
23 | Filters,
24 | ConversationHandler,
25 | )
26 |
27 | from telegram.ext.dispatcher import run_async
28 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton, ForceReply
29 |
30 | from acutebot import dp, typing
31 | from acutebot.helpers import strings as st
32 |
33 | base_url = "http://subtitle.iamidiotareyoutoo.com"
34 |
35 |
36 | @run_async
37 | @typing
38 | def subs_entry(update, context):
39 |
40 | update.effective_message.reply_text(
41 | st.TOSEARCHSUBS, reply_markup=ForceReply(selective=True),
42 | )
43 |
44 | return 1
45 |
46 |
47 | @run_async
48 | @typing
49 | def getsubs(update, context):
50 | msg = update.effective_message
51 | text = update.message.text
52 | query = text.replace(" ", "%20")
53 |
54 | res = r.get(f"{base_url}/search/{query}/1").json()
55 | resarr = res.get("r")
56 | if len(resarr) <= 0:
57 | msg.reply_text(st.NOT_FOUND)
58 | return -1
59 |
60 | keyb = []
61 | for x in resarr:
62 | keyb.append(
63 | [
64 | InlineKeyboardButton(
65 | text=x.get("SIQ"), callback_data=f"subs_{x.get('DMCA_ID')}"
66 | )
67 | ]
68 | )
69 | msg.reply_text(st.SUBS_STR.format(text), reply_markup=InlineKeyboardMarkup(keyb))
70 | return ConversationHandler.END
71 |
72 |
73 | @run_async
74 | def subsbutton(update, context):
75 | query = update.callback_query
76 | chat = update.effective_chat
77 | query.answer()
78 | tm = query.message.reply_text("⌛ Hold on . . .")
79 | query.message.delete()
80 |
81 | subs_id = query.data.split("_")[1]
82 | res = r.get(f"{base_url}/get/{subs_id}/").json()
83 | dl_link = base_url + res.get("DL_LINK")
84 | dl_content_name = res.get("DL_SUB_NAME")
85 |
86 | dl_content = BytesIO(r.get(dl_link).content)
87 | dl_content.name = dl_content_name
88 | context.bot.sendDocument(chat.id, dl_content, caption="Subtitle via @acutebot 🎸")
89 | tm.delete()
90 |
91 |
92 | @run_async
93 | @typing
94 | def cancel(update, context):
95 | context.bot.sendMessage(update.effective_chat.id, (st.CANCEL))
96 | return ConversationHandler.END
97 |
98 |
99 | SUBS_HANDLER = ConversationHandler(
100 | entry_points=[CommandHandler("subtitle", subs_entry)],
101 | states={1: [MessageHandler(Filters.text & ~Filters.command, getsubs)]},
102 | fallbacks=[CommandHandler("cancel", cancel)],
103 | conversation_timeout=120,
104 | )
105 |
106 | SUBS_CALLBACK_HANDLER = CallbackQueryHandler(subsbutton, pattern=r"subs_")
107 |
108 |
109 | dp.add_handler(SUBS_HANDLER)
110 | dp.add_handler(SUBS_CALLBACK_HANDLER)
111 |
--------------------------------------------------------------------------------
/acutebot/funcs/lyrics.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import os, lyricsgenius
17 |
18 | from telegram.ext.dispatcher import run_async
19 | from telegram.ext import CommandHandler, MessageHandler, Filters, ConversationHandler
20 | from telegram.error import BadRequest
21 | from telegram import ForceReply
22 |
23 | from acutebot import dp, typing, LOG, GENIUS
24 | from acutebot.helpers import strings as st
25 |
26 | ARTIST, LYRICS = range(2)
27 | SONGDICT = {}
28 |
29 | if GENIUS is not None:
30 | genius = lyricsgenius.Genius(GENIUS)
31 |
32 |
33 | @run_async
34 | @typing
35 | def songname(update, context):
36 | update.effective_message.reply_text(
37 | st.SONGNAME, reply_markup=ForceReply(selective=True)
38 | )
39 |
40 | return ARTIST
41 |
42 |
43 | @run_async
44 | @typing
45 | def artistname(update, context):
46 | msg = update.effective_message
47 | user = update.effective_user
48 | song = update.message.text
49 |
50 | SONGDICT[user.id] = song
51 | msg.reply_text(st.ARTISTNAME, reply_markup=ForceReply(selective=True))
52 |
53 | return LYRICS
54 |
55 |
56 | @run_async
57 | @typing
58 | def sendlyrics(update, context):
59 | chat = update.effective_chat
60 | msg = update.effective_message
61 | user = update.effective_user
62 | artist = update.message.text
63 |
64 | try:
65 | song = SONGDICT[user.id]
66 | except KeyError:
67 | msg.reply_text(st.LYRICS_ERR)
68 | return -1
69 | rep = msg.reply_text("🔍 Looking for lyrics . . .")
70 | lyrics = genius.search_song(song, artist)
71 | if lyrics is None:
72 | msg.reply_text(st.LYRIC_NOT_FOUND)
73 | rep.delete()
74 | return -1
75 | try:
76 | msg.reply_text(f"🎸 {song} by {artist}:\n\n{lyrics.lyrics}")
77 |
78 | except BadRequest as excp:
79 | if excp.message == "Message is too long":
80 | msg.reply_text(st.LYRICS_TOO_BIG)
81 | with open("acute-lyrics.txt", "w+") as f:
82 | f.write(f"🎧 {song} by {artist}\n\n{lyrics.lyrics}")
83 | context.bot.sendDocument(
84 | chat_id=chat.id,
85 | document=open("acute-lyrics.txt", "rb"),
86 | caption=f"🎸 {song} - {artist}",
87 | )
88 | os.remove("acute-lyrics.txt")
89 | else:
90 | LOG.error(excp.message)
91 |
92 | rep.delete()
93 | del SONGDICT[user.id]
94 | return -1
95 |
96 |
97 | @run_async
98 | @typing
99 | def cancel(update, context):
100 | context.bot.sendMessage(update.effective_chat.id, (st.CANCEL))
101 | return ConversationHandler.END
102 |
103 |
104 | LYRICS_HANDLER = ConversationHandler(
105 | entry_points=[CommandHandler("lyrics", songname)],
106 | states={
107 | ARTIST: [MessageHandler(Filters.text & ~Filters.command, artistname)],
108 | LYRICS: [MessageHandler(Filters.text & ~Filters.command, sendlyrics)],
109 | },
110 | fallbacks=[CommandHandler("cancel", cancel)],
111 | conversation_timeout=120,
112 | )
113 |
114 | dp.add_handler(LYRICS_HANDLER)
115 |
--------------------------------------------------------------------------------
/acutebot/funcs/manga.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import requests as r
17 |
18 | from telegram.ext import (
19 | MessageHandler,
20 | CommandHandler,
21 | Filters,
22 | ConversationHandler,
23 | CallbackQueryHandler,
24 | )
25 |
26 | from telegram.ext.dispatcher import run_async
27 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton, ForceReply
28 |
29 | from acutebot import dp, typing
30 | from acutebot.helpers import strings as st
31 | from acutebot.helpers.parsedata import sort_caps
32 | from acutebot.helpers.keyboard import keyboard
33 |
34 | base_url = "https://kitsu.io/api/edge"
35 | tempdict = {}
36 |
37 |
38 | @run_async
39 | @typing
40 | def manga_entry(update, context):
41 | update.effective_message.reply_text(
42 | st.TOSEARCH_MANGA, reply_markup=ForceReply(selective=True),
43 | )
44 |
45 | return 1
46 |
47 |
48 | @run_async
49 | @typing
50 | def manga(update, context):
51 | msg = update.message
52 | user = update.effective_user
53 | query = msg.text.replace(" ", "%20")
54 |
55 | res = r.get(f"{base_url}/manga?filter%5Btext%5D={query}")
56 | if res.status_code != 200:
57 | msg.reply_text(st.API_ERR)
58 | return -1
59 |
60 | res = res.json()["data"]
61 | if len(res) <= 0:
62 | msg.reply_text(st.NOT_FOUND)
63 | return -1
64 |
65 | # Add results array with user's id as key
66 | tempdict[user.id] = res
67 |
68 | keyb = []
69 | for x in enumerate(res):
70 | titles = x[1]["attributes"]["titles"]
71 | keyb.append(
72 | [
73 | InlineKeyboardButton(
74 | text=f"{titles.get('en') if titles.get('en') else titles.get('ja_jp')}",
75 | callback_data=f"manga_{x[0]}_{user.id}",
76 | )
77 | ]
78 | )
79 |
80 | msg.reply_text(
81 | f"🔍 Search results for {msg.text}:",
82 | reply_markup=InlineKeyboardMarkup(keyb[:6]),
83 | )
84 |
85 | return ConversationHandler.END
86 |
87 |
88 | @run_async
89 | def manga_button(update, context):
90 | query = update.callback_query
91 | chat = update.effective_chat
92 | user = update.effective_user
93 |
94 | spl = query.data.split("_")
95 | x, user_id = int(spl[1]), int(spl[2])
96 | if user.id != user_id:
97 | return query.answer(st.NOT_ALLOWED, show_alert=True)
98 |
99 | try:
100 | res = tempdict[user_id]
101 | except KeyError:
102 | return query.answer(st.KEYERROR, show_alert=True)
103 |
104 | query.answer("Hold on...")
105 | query.message.delete()
106 |
107 | data = res[x]["attributes"]
108 | caption = st.MANGA_STR.format(
109 | data["titles"].get("en", ""),
110 | data["titles"].get("ja_jp", ""),
111 | data.get("subtype", "N/A"),
112 | data.get("averageRating", "N/A"),
113 | data.get("status", "N/A"),
114 | data.get("startDate", "N/A"),
115 | data.get("endDate", "N/A"),
116 | data.get("volumeCount", "N/A"),
117 | data.get("chapterCount", "N/A"),
118 | data.get("serialization", "N/A"),
119 | data.get("synopsis", "N/A"),
120 | )
121 |
122 | if data.get("posterImage"):
123 | context.bot.sendPhoto(
124 | chat_id=chat.id,
125 | photo=data["posterImage"]["original"],
126 | caption=sort_caps(caption, c_id=data["slug"], manga=True),
127 | reply_markup=InlineKeyboardMarkup(keyboard(manga_id=data["slug"],)),
128 | timeout=60,
129 | disable_web_page_preview=True,
130 | )
131 |
132 | else:
133 | context.bot.sendMessage(
134 | chat.id,
135 | text=caption,
136 | reply_markup=InlineKeyboardMarkup(keyboard(manga_id=data["slug"],)),
137 | disable_web_page_preview=True,
138 | )
139 | del tempdict[user_id]
140 |
141 |
142 | @run_async
143 | @typing
144 | def cancel(update, context):
145 | context.bot.sendMessage(update.effective_chat.id, (st.CANCEL))
146 | return ConversationHandler.END
147 |
148 |
149 | MANGA_HANDLER = ConversationHandler(
150 | entry_points=[CommandHandler("manga", manga_entry)],
151 | states={1: [MessageHandler(Filters.text & ~Filters.command, manga)]},
152 | fallbacks=[CommandHandler("cancel", cancel)],
153 | conversation_timeout=120,
154 | )
155 | MANGA_BUTTON_HANDLER = CallbackQueryHandler(manga_button, pattern=r"manga_")
156 |
157 | dp.add_handler(MANGA_HANDLER)
158 | dp.add_handler(MANGA_BUTTON_HANDLER)
159 |
--------------------------------------------------------------------------------
/acutebot/funcs/anime.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import requests as r
17 |
18 | from telegram.ext import (
19 | MessageHandler,
20 | CommandHandler,
21 | Filters,
22 | ConversationHandler,
23 | CallbackQueryHandler,
24 | )
25 |
26 | from telegram.ext.dispatcher import run_async
27 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton, ForceReply
28 |
29 | from acutebot import dp, typing
30 | from acutebot.helpers import strings as st
31 | from acutebot.helpers.parsedata import sort_caps
32 | from acutebot.helpers.keyboard import keyboard
33 |
34 | base_url = "https://kitsu.io/api/edge"
35 | tempdict = {}
36 |
37 |
38 | @run_async
39 | @typing
40 | def anime_entry(update, context):
41 | update.effective_message.reply_text(
42 | st.TOSEARCH_ANIME, reply_markup=ForceReply(selective=True),
43 | )
44 |
45 | return 1
46 |
47 |
48 | @run_async
49 | @typing
50 | def anime(update, context):
51 | msg = update.message
52 | user = update.effective_user
53 | query = msg.text.replace(" ", "%20")
54 |
55 | res = r.get(f"{base_url}/anime?filter%5Btext%5D={query}")
56 | if res.status_code != 200:
57 | msg.reply_text(st.API_ERR)
58 | return -1
59 |
60 | res = res.json()["data"]
61 | if len(res) <= 0:
62 | msg.reply_text(st.NOT_FOUND)
63 | return -1
64 |
65 | # Add results array with user's id as key
66 | tempdict[user.id] = res
67 |
68 | keyb = []
69 | for x in enumerate(res):
70 | titles = x[1]["attributes"]["titles"]
71 | keyb.append(
72 | [
73 | InlineKeyboardButton(
74 | text=f"{titles.get('en') if titles.get('en') else titles.get('ja_jp')}",
75 | callback_data=f"anime_{x[0]}_{user.id}",
76 | )
77 | ]
78 | )
79 |
80 | msg.reply_text(
81 | f"🔍 Search results for {msg.text}:",
82 | reply_markup=InlineKeyboardMarkup(keyb[:6]),
83 | )
84 |
85 | return ConversationHandler.END
86 |
87 |
88 | @run_async
89 | def anime_button(update, context):
90 | query = update.callback_query
91 | chat = update.effective_chat
92 | user = update.effective_user
93 |
94 | spl = query.data.split("_")
95 | x, user_id = int(spl[1]), int(spl[2])
96 | if user.id != user_id:
97 | return query.answer(st.NOT_ALLOWED, show_alert=True)
98 |
99 | try:
100 | res = tempdict[user_id]
101 | except KeyError:
102 | return query.answer(st.KEYERROR, show_alert=True)
103 |
104 | query.answer("Hold on...")
105 | query.message.delete()
106 |
107 | data = res[x]["attributes"]
108 | caption = st.ANIME_STR.format(
109 | data["titles"].get("en", ""),
110 | data["titles"].get("ja_jp", ""),
111 | data.get("subtype", "N/A"),
112 | data.get("ageRatingGuide", "N/A"),
113 | data.get("averageRating", "N/A"),
114 | data.get("status", "N/A"),
115 | data.get("startDate", "N/A"),
116 | data.get("endDate", "N/A"),
117 | data.get("episodeLength", "N/A"),
118 | data.get("episodeCount", "N/A"),
119 | data.get("synopsis", "N/A"),
120 | )
121 |
122 | if data.get("posterImage"):
123 | context.bot.sendPhoto(
124 | chat_id=chat.id,
125 | photo=data["posterImage"]["original"],
126 | caption=sort_caps(caption, c_id=res[x]["id"], anime=True),
127 | reply_markup=InlineKeyboardMarkup(
128 | keyboard(
129 | title=data["titles"].get("en"),
130 | anime_ytkey=data.get("youtubeVideoId"),
131 | anime_id=res[x]["id"],
132 | )
133 | ),
134 | timeout=60,
135 | disable_web_page_preview=True,
136 | )
137 |
138 | else:
139 | context.bot.sendMessage(
140 | chat.id,
141 | text=caption,
142 | reply_markup=InlineKeyboardMarkup(
143 | keyboard(
144 | title=data["titles"].get("en"),
145 | anime_ytkey=data.get("youtubeVideoId"),
146 | anime_id=res[x]["id"],
147 | )
148 | ),
149 | disable_web_page_preview=True,
150 | )
151 | del tempdict[user_id]
152 |
153 |
154 | @run_async
155 | @typing
156 | def cancel(update, context):
157 | context.bot.sendMessage(update.effective_chat.id, (st.CANCEL))
158 | return ConversationHandler.END
159 |
160 |
161 | ANIME_HANDLER = ConversationHandler(
162 | entry_points=[CommandHandler("anime", anime_entry)],
163 | states={1: [MessageHandler(Filters.text & ~Filters.command, anime)]},
164 | fallbacks=[CommandHandler("cancel", cancel)],
165 | conversation_timeout=120,
166 | )
167 | AN_BUTTON_HANDLER = CallbackQueryHandler(anime_button, pattern=r"anime_")
168 |
169 | dp.add_handler(ANIME_HANDLER)
170 | dp.add_handler(AN_BUTTON_HANDLER)
171 |
--------------------------------------------------------------------------------
/acutebot/funcs/movies.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import requests as r
17 |
18 | from telegram.ext import (
19 | MessageHandler,
20 | CommandHandler,
21 | Filters,
22 | ConversationHandler,
23 | CallbackQueryHandler,
24 | )
25 |
26 | from telegram.ext.dispatcher import run_async
27 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton, ForceReply
28 |
29 | from acutebot import dp, TMDBAPI, typing
30 | from acutebot.helpers import strings as st
31 | from acutebot.helpers.parsedata import byname, currency, sort_caps
32 | from acutebot.helpers.keyboard import keyboard
33 |
34 |
35 | base_url = "https://api.themoviedb.org/3"
36 | pic_url = "https://image.tmdb.org/t/p"
37 |
38 |
39 | def moviedata(c_id):
40 | """
41 | Parse movie data for the id and return class obj
42 | """
43 | payload = {"api_key": TMDBAPI, "language": "en-US", "append_to_response": "videos"}
44 |
45 | data = r.get(f"{base_url}/movie/{c_id}?", params=payload).json()
46 |
47 | class res:
48 |
49 | c_id = data.get("id")
50 | title = data.get("title")
51 | tagline = data.get("tagline")
52 | status = data.get("status")
53 | genres = byname(data.get("genres"))
54 | language = byname(data.get("spoken_languages"))
55 | runtime = data.get("runtime")
56 | budget = currency(data.get("budget"))
57 | revenue = currency(data.get("revenue"))
58 | release = data.get("release_date")
59 | rating = data.get("vote_average")
60 | popularity = data.get("popularity")
61 | overview = data.get("overview")
62 |
63 | # Keyboard objects
64 | posterpath = data.get("poster_path")
65 | homepage = data.get("homepage")
66 | imdbid = data.get("imdb_id")
67 | ytkey = data.get("videos")
68 |
69 | return res
70 |
71 |
72 | @run_async
73 | @typing
74 | def movie_entry(update, context):
75 |
76 | update.effective_message.reply_text(
77 | st.TOSEARCHMOVIE, reply_markup=ForceReply(selective=True),
78 | )
79 |
80 | return 1
81 |
82 |
83 | @run_async
84 | @typing
85 | def movie(update, context):
86 | msg = update.message
87 | user = update.effective_user
88 | query = msg.text.replace(" ", "%20")
89 |
90 | results = r.get(
91 | f"{base_url}/search/movie?api_key={TMDBAPI}"
92 | + f"&language=en&query={query}"
93 | + "&page=1&include_adult=true"
94 | )
95 |
96 | if results.status_code != 200:
97 | msg.reply_text(st.API_ERR)
98 | return -1
99 |
100 | results = results.json()["results"]
101 | if len(results) <= 0:
102 | msg.reply_text(st.NOT_FOUND)
103 | return -1
104 |
105 | keyb = []
106 | for x in results:
107 | keyb.append(
108 | [
109 | InlineKeyboardButton(
110 | text=x.get("title"), callback_data=f"movie_{x.get('id')}_{user.id}"
111 | )
112 | ]
113 | )
114 | msg.reply_text(
115 | f"🔍 Search results for {msg.text}:",
116 | reply_markup=InlineKeyboardMarkup(keyb[:6]),
117 | )
118 |
119 | return ConversationHandler.END
120 |
121 |
122 | @run_async
123 | def movie_button(update, context):
124 | query = update.callback_query
125 | chat = update.effective_chat
126 | user = update.effective_user
127 |
128 | spl = query.data.split("_")
129 | c_id, user_id = spl[1], spl[2]
130 | if user.id != int(user_id):
131 | return query.answer(st.NOT_ALLOWED, show_alert=True)
132 |
133 | query.answer("Hold on...")
134 | query.message.delete()
135 |
136 | res = moviedata(c_id)
137 | caption = st.MOVIE_STR.format(
138 | res.title,
139 | res.tagline,
140 | res.status,
141 | res.genres,
142 | res.language,
143 | res.runtime,
144 | res.budget,
145 | res.revenue,
146 | res.release,
147 | res.rating,
148 | res.popularity,
149 | res.overview,
150 | )
151 |
152 | if res.posterpath:
153 | context.bot.sendPhoto(
154 | chat_id=chat.id,
155 | photo=f"{pic_url}/w500/{res.posterpath}",
156 | caption=sort_caps(caption, c_id=res.c_id, mv=True),
157 | reply_markup=InlineKeyboardMarkup(
158 | keyboard(res.ytkey, res.homepage, res.title, res.imdbid)
159 | ),
160 | timeout=60,
161 | disable_web_page_preview=True,
162 | )
163 | else:
164 | context.bot.sendMessage(
165 | chat.id,
166 | text=caption,
167 | reply_markup=InlineKeyboardMarkup(
168 | keyboard(res.ytkey, res.homepage, res.title, res.imdbid),
169 | disable_web_page_preview=True,
170 | ),
171 | )
172 |
173 |
174 | @run_async
175 | @typing
176 | def cancel(update, context):
177 | context.bot.sendMessage(update.effective_chat.id, (st.CANCEL))
178 | return ConversationHandler.END
179 |
180 |
181 | MOVIE_HANDLER = ConversationHandler(
182 | entry_points=[CommandHandler("movies", movie_entry)],
183 | states={1: [MessageHandler(Filters.text & ~Filters.command, movie)]},
184 | fallbacks=[CommandHandler("cancel", cancel)],
185 | conversation_timeout=120,
186 | )
187 | MV_BUTTON_HANDLER = CallbackQueryHandler(movie_button, pattern=r"movie_")
188 |
189 | dp.add_handler(MOVIE_HANDLER)
190 | dp.add_handler(MV_BUTTON_HANDLER)
191 |
--------------------------------------------------------------------------------
/acutebot/funcs/miscs.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import requests as r
17 | import subprocess, random
18 | from time import sleep
19 |
20 | from acutebot.helpers.database import users_sql as sql
21 | from acutebot.helpers.database.favorites_sql import fav_count
22 | import acutebot.helpers.strings as st
23 | from acutebot import dp, typing, DEV_ID, LOG
24 |
25 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton, TelegramError
26 | from telegram.ext.dispatcher import run_async
27 | from telegram.ext import CommandHandler, MessageHandler, Filters
28 | from telegram.error import BadRequest
29 |
30 |
31 | @run_async
32 | @typing
33 | def get_ip(update, context):
34 | res = r.get("http://ipinfo.io/ip")
35 | update.message.reply_text(res.text)
36 |
37 |
38 | @run_async
39 | def rmemes(update, context):
40 | msg = update.effective_message
41 | chat = update.effective_chat
42 |
43 | SUBREDS = [
44 | "meirl",
45 | "dankmemes",
46 | "AdviceAnimals",
47 | "memes",
48 | "meme",
49 | "memes_of_the_dank",
50 | "PornhubComments",
51 | "teenagers",
52 | "memesIRL",
53 | "insanepeoplefacebook",
54 | "terriblefacebookmemes",
55 | "animememes",
56 | "Animemes",
57 | "memeeconomy",
58 | ]
59 |
60 | subreddit = random.choice(SUBREDS)
61 | res = r.get(f"https://meme-api.herokuapp.com/gimme/{subreddit}")
62 |
63 | if res.status_code != 200: # Like if api is down?
64 | return msg.reply_text(st.API_ERR)
65 | res = res.json()
66 |
67 | keyb = [[InlineKeyboardButton(text=f"r/{res['subreddit']}", url=res["postLink"])]]
68 | try:
69 | context.bot.send_chat_action(chat.id, "upload_photo")
70 | context.bot.send_photo(
71 | chat.id,
72 | photo=res["url"],
73 | caption=(res["title"]),
74 | reply_markup=InlineKeyboardMarkup(keyb),
75 | timeout=60,
76 | )
77 |
78 | except BadRequest as excp:
79 | LOG.error(excp)
80 |
81 |
82 | @run_async
83 | def stats(update, context):
84 | msg = update.effective_message
85 | return msg.reply_text(
86 | st.STATS.format(sql.users_count(), fav_count()), parse_mode=None,
87 | )
88 |
89 |
90 | @run_async
91 | def greet(update, context):
92 | msg = update.effective_message
93 | user = update.effective_user
94 | chat = update.effective_chat
95 |
96 | new_members = msg.new_chat_members
97 | for new_mem in new_members:
98 | if new_mem.id == context.bot.id:
99 | msg.reply_text(st.GREET.format(user.first_name, chat.title))
100 |
101 |
102 | @run_async
103 | def shell(update, context):
104 | bot = context.bot
105 | msg = update.effective_message
106 | chat = update.effective_chat
107 | command = " ".join(context.args).split()
108 | rep = msg.reply_text("Running command...")
109 | try:
110 | res = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,)
111 | stdout, stderr = res.communicate()
112 | result = str(stdout.decode().strip()) + str(stderr.decode().strip())
113 | bot.editMessageText("" + result + "
", chat.id, rep.message_id)
114 | except Exception as e:
115 | bot.editMessageText(str(e), chat.id, rep.message_id)
116 |
117 |
118 | @run_async
119 | def broadcast(update, context):
120 | to_send = update.effective_message.text.split(None, 1)
121 | if len(to_send) >= 2:
122 | users = sql.get_all_users() or []
123 | failed = 0
124 | for user in users:
125 | try:
126 | context.bot.sendMessage(int(user.user_id), to_send[1])
127 | sleep(0.1)
128 | except TelegramError:
129 | failed += 1
130 | LOG.warning(
131 | "Couldn't send broadcast to %s, username %s",
132 | str(user.user_id),
133 | str(user.username),
134 | )
135 |
136 | update.effective_message.reply_text(
137 | "Broadcast complete. {} users failed to receive the message, probably "
138 | "due to being Blocked.".format(failed)
139 | )
140 |
141 |
142 |
143 | @run_async
144 | def log_user(update, context):
145 | msg = update.effective_message
146 |
147 | sql.update_user(msg.from_user.id, msg.from_user.username)
148 |
149 | if msg.reply_to_message:
150 | sql.update_user(
151 | msg.reply_to_message.from_user.id, msg.reply_to_message.from_user.username,
152 | )
153 |
154 | if msg.forward_from:
155 | sql.update_user(msg.forward_from.id, msg.forward_from.username)
156 |
157 |
158 | IP_HANDLER = CommandHandler("ip", get_ip, filters=Filters.chat(DEV_ID))
159 | REDDIT_HANDLER = CommandHandler("reddit", rmemes)
160 | STATS_HANDLER = CommandHandler("stats", stats, filters=Filters.user(DEV_ID))
161 | GREET_HANDLER = MessageHandler(Filters.status_update.new_chat_members, greet)
162 | SHELL_HANDLER = CommandHandler("shell", shell, filters=Filters.user(DEV_ID))
163 | LOG_HANDLER = MessageHandler(Filters.all, log_user)
164 | BROADCAST_HANDLER = CommandHandler(
165 | "broadcast", broadcast, filters=Filters.user(DEV_ID)
166 | )
167 |
168 | dp.add_handler(IP_HANDLER)
169 | dp.add_handler(REDDIT_HANDLER)
170 | dp.add_handler(STATS_HANDLER)
171 | dp.add_handler(GREET_HANDLER)
172 | dp.add_handler(SHELL_HANDLER)
173 | dp.add_handler(LOG_HANDLER, 1)
174 | dp.add_handler(BROADCAST_HANDLER)
175 |
--------------------------------------------------------------------------------
/acutebot/funcs/tvseries.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import requests as r
17 |
18 | from telegram.ext import (
19 | MessageHandler,
20 | CommandHandler,
21 | Filters,
22 | ConversationHandler,
23 | CallbackQueryHandler,
24 | )
25 |
26 | from telegram.ext.dispatcher import run_async
27 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton, ForceReply
28 |
29 | from acutebot import dp, TMDBAPI, typing
30 | from acutebot.helpers import strings as st
31 | from acutebot.helpers.parsedata import byname, byindex, sort_caps, tvruntime
32 | from acutebot.helpers.keyboard import keyboard
33 |
34 | base_url = "https://api.themoviedb.org/3"
35 | pic_url = "https://image.tmdb.org/t/p"
36 |
37 |
38 | def tvdata(c_id):
39 | """
40 | Parse TV shows data for the id and return class obj
41 | """
42 | payload = {"api_key": TMDBAPI, "language": "en-US", "append_to_response": "videos"}
43 |
44 | data = r.get(f"{base_url}/tv/{c_id}?", params=payload).json()
45 |
46 | class res:
47 |
48 | c_id = data.get("id")
49 | title = data.get("original_name")
50 | creator = byindex(data.get("created_by"))
51 | genres = byname(data.get("genres"))
52 | language = data.get("original_language")
53 | runtime = tvruntime(data.get("episode_run_time"))
54 | faired = data.get("first_air_date")
55 | laired = data.get("last_air_date")
56 | status = data.get("status")
57 | seasons = data.get("number_of_seasons")
58 | numeps = data.get("number_of_episodes")
59 | rating = data.get("vote_average")
60 | company = byindex(data.get("production_companies"))
61 | overview = data.get("overview")
62 |
63 | # Keyboard objects
64 | posterpath = data.get("poster_path")
65 | homepage = data.get("homepage")
66 | ytkey = data.get("videos")
67 |
68 | return res
69 |
70 |
71 | @run_async
72 | @typing
73 | def tv_entry(update, context):
74 |
75 | update.effective_message.reply_text(
76 | st.TOSEARCHTV, reply_markup=ForceReply(selective=True),
77 | )
78 |
79 | return 1
80 |
81 |
82 | @run_async
83 | @typing
84 | def tv(update, context):
85 | msg = update.message
86 | user = update.effective_user
87 | query = msg.text.replace(" ", "%20")
88 |
89 | results = r.get(
90 | f"{base_url}/search/tv?api_key={TMDBAPI}"
91 | + f"&language=en&query={query}"
92 | + "&page=1&include_adult=true"
93 | )
94 |
95 | if results.status_code != 200:
96 | msg.reply_text(st.API_ERR)
97 | return -1
98 |
99 | results = results.json()["results"]
100 |
101 | if len(results) <= 0:
102 | msg.reply_text(st.NOT_FOUND)
103 | return -1
104 |
105 | keyb = []
106 | for x in results:
107 | keyb.append(
108 | [
109 | InlineKeyboardButton(
110 | text=x.get("original_name"),
111 | callback_data=f"tv_{x.get('id')}_{user.id}",
112 | )
113 | ]
114 | )
115 | msg.reply_text(
116 | f"🔍 Search results for {msg.text}:",
117 | reply_markup=InlineKeyboardMarkup(keyb[:6]),
118 | )
119 |
120 | return ConversationHandler.END
121 |
122 |
123 | @run_async
124 | def tv_button(update, context):
125 | query = update.callback_query
126 | chat = update.effective_chat
127 | user = update.effective_user
128 |
129 | spl = query.data.split("_")
130 | c_id, user_id = spl[1], spl[2]
131 | if user.id != int(user_id):
132 | return query.answer(st.NOT_ALLOWED, show_alert=True)
133 |
134 | query.answer("Hold on...")
135 | query.message.delete()
136 |
137 | res = tvdata(c_id)
138 | caption = st.TV_STR.format(
139 | res.title,
140 | res.creator,
141 | res.genres,
142 | res.language,
143 | res.runtime,
144 | res.faired,
145 | res.laired,
146 | res.status,
147 | res.seasons,
148 | res.numeps,
149 | res.rating,
150 | res.company,
151 | res.overview,
152 | )
153 |
154 | if res.posterpath:
155 | context.bot.sendPhoto(
156 | chat_id=chat.id,
157 | photo=f"{pic_url}/w500/{res.posterpath}",
158 | caption=sort_caps(caption, c_id=res.c_id, tv=True),
159 | reply_markup=InlineKeyboardMarkup(
160 | keyboard(res.ytkey, res.homepage, res.title)
161 | ),
162 | disable_web_page_preview=True,
163 | timeout=60,
164 | )
165 | else:
166 | context.bot.sendMessage(
167 | chat.id,
168 | text=caption,
169 | reply_markup=InlineKeyboardMarkup(
170 | keyboard(res.ytkey, res.homepage, res.title),
171 | disable_web_page_preview=True,
172 | ),
173 | )
174 |
175 |
176 | @run_async
177 | @typing
178 | def cancel(update, context):
179 | context.bot.sendMessage(update.effective_chat.id, (st.CANCEL))
180 | return ConversationHandler.END
181 |
182 |
183 | TV_HANDLER = ConversationHandler(
184 | entry_points=[CommandHandler("tvshows", tv_entry)],
185 | states={1: [MessageHandler(Filters.text & ~Filters.command, tv)]},
186 | fallbacks=[CommandHandler("cancel", cancel)],
187 | conversation_timeout=120,
188 | )
189 | TV_BUTTON_HANDLER = CallbackQueryHandler(tv_button, pattern=r"tv_")
190 |
191 |
192 | dp.add_handler(TV_HANDLER)
193 | dp.add_handler(TV_BUTTON_HANDLER)
194 |
--------------------------------------------------------------------------------
/acutebot/funcs/music.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import os, deezloader, mutagen
17 | from deezloader.exceptions import BadCredentials, TrackNotFound, NoDataApi
18 |
19 | from pathlib import Path
20 | from telegram.ext.dispatcher import run_async
21 | from telegram.ext import CommandHandler, MessageHandler, Filters, ConversationHandler
22 | from telegram import ForceReply, ReplyKeyboardMarkup
23 | from pyrogram import Client
24 |
25 | from acutebot import dp, typing, ARLTOKEN, LOG, APIID, APIHASH
26 | from acutebot.helpers import strings as st
27 |
28 | MUSIC, ARTIST, SENDMUSIC = range(3)
29 | MUSICDICT = {}
30 |
31 | if ARLTOKEN is not None:
32 | try:
33 | downloa = deezloader.Login(ARLTOKEN)
34 | except BadCredentials:
35 | print("Deezer token is dead :(")
36 |
37 |
38 | @run_async
39 | @typing
40 | def musicq(update, context):
41 | reply_keyboard = [["🎵 256KBs", "🎧 320KBs", "🎶 FLAC"]]
42 |
43 | update.effective_message.reply_text(
44 | st.MUSICQ,
45 | reply_markup=ReplyKeyboardMarkup(
46 | reply_keyboard, one_time_keyboard=True, selective=True
47 | ),
48 | )
49 | return MUSIC
50 |
51 |
52 | @run_async
53 | @typing
54 | def music(update, context):
55 | user = update.effective_user
56 | msg = update.effective_message
57 | songq = update.message.text
58 |
59 | if songq in ("🎵 256KBs", "🎧 320KBs", "🎶 FLAC"):
60 | # save quality data in temp.dict:
61 | MUSICDICT[user.id] = {"q": songq}
62 | msg.reply_text(st.MUSICNAME, reply_markup=ForceReply(selective=True))
63 | return ARTIST
64 | msg.reply_text(st.INVALIDCAT)
65 | return -1
66 |
67 |
68 | @run_async
69 | @typing
70 | def _artist(update, context):
71 | user = user = update.effective_user
72 | msg = update.effective_message
73 | musicn = update.message.text
74 | # update music title in dict:
75 | MUSICDICT[user.id]["mn"] = musicn
76 | msg.reply_text(st.ARTISTNAME, reply_markup=ForceReply(selective=True))
77 | return SENDMUSIC
78 |
79 |
80 | @run_async
81 | def sendmusic(update, context):
82 | user = update.effective_user
83 | msg = update.effective_message
84 | chat = update.effective_chat
85 |
86 | artist = update.message.text
87 | try:
88 | song = MUSICDICT[user.id]["mn"]
89 | quality = MUSICDICT[user.id]["q"]
90 | except KeyError:
91 | msg.reply_text(st.LYRICS_ERR)
92 | return -1
93 |
94 | if quality == "🎧 320KBs":
95 | ql = "MP3_320"
96 | elif quality == "🎶 FLAC":
97 | ql = "FLAC"
98 | elif quality == "🎵 256KBs":
99 | ql = "MP3_256"
100 |
101 | try:
102 | context.bot.send_chat_action(chat.id, "upload_document")
103 | file = downloa.download_name(
104 | artist=artist,
105 | song=song,
106 | output="temp",
107 | quality=ql,
108 | recursive_quality=True,
109 | recursive_download=True,
110 | not_interface=True,
111 | )
112 | except (TrackNotFound, NoDataApi):
113 | msg.reply_text(st.MUSICNOTFOUND)
114 | return -1
115 |
116 | try:
117 | # Fetch correct details from metadata:
118 | aud = mutagen.File(file)
119 | title = aud.get("title")
120 | if title:
121 | title = str(title[0])
122 | artist = aud.get("artist")
123 | if artist:
124 | artist = str(artist[0])
125 | duration = aud.get("length")
126 | if duration:
127 | duration = int(duration[0])
128 |
129 | if Path(file).stat().st_size < 50000000:
130 | rep = msg.reply_text(st.UPLOAD_BOTAPI)
131 | context.bot.sendAudio(
132 | chat.id,
133 | open(file, "rb"),
134 | caption="Via @acutebot 🎸",
135 | title=title,
136 | performer=artist,
137 | duration=duration,
138 | timeout=120,
139 | )
140 | else:
141 | rep = msg.reply_text(st.UPLOAD_MTPROTO)
142 | send_file_pyro(context.bot.token, file, chat.id, title, artist, duration)
143 | rep.delete()
144 |
145 | except Exception as e:
146 | LOG.error(e)
147 |
148 | if os.path.isfile(file):
149 | os.remove(file)
150 | del MUSICDICT[user.id]
151 | return ConversationHandler.END
152 |
153 |
154 | def send_file_pyro(bot_token, file, chatid, title, artist, duration):
155 | bot = Client("acute", bot_token=bot_token, api_id=APIID, api_hash=APIHASH)
156 | with bot:
157 | bot.send_audio(
158 | chat_id=chatid,
159 | audio=open(file, "rb"),
160 | caption="Via @acutebot 🎸",
161 | title=title,
162 | duration=duration,
163 | performer=artist,
164 | )
165 |
166 |
167 | @run_async
168 | @typing
169 | def cancel(update, context):
170 | context.bot.sendMessage(update.effective_chat.id, (st.CANCEL))
171 | return ConversationHandler.END
172 |
173 |
174 | MUSIC_HANDLER = ConversationHandler(
175 | entry_points=[CommandHandler("music", musicq)],
176 | states={
177 | MUSIC: [MessageHandler(Filters.text & ~Filters.command, music)],
178 | ARTIST: [MessageHandler(Filters.text & ~Filters.command, _artist)],
179 | SENDMUSIC: [MessageHandler(Filters.text & ~Filters.command, sendmusic)],
180 | },
181 | fallbacks=[CommandHandler("cancel", cancel)],
182 | conversation_timeout=120,
183 | )
184 |
185 |
186 | dp.add_handler(MUSIC_HANDLER)
187 |
--------------------------------------------------------------------------------
/acutebot/__main__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | import os, sys, importlib
17 | from threading import Thread
18 |
19 | from acutebot import LOG, dp, updater, DEV_ID
20 | from acutebot.funcs import ALL_FUNCS
21 | import acutebot.helpers.strings as st
22 |
23 |
24 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton
25 | from telegram.ext.dispatcher import run_async
26 | from telegram.ext import CommandHandler, CallbackQueryHandler, Filters
27 |
28 |
29 | # Import all funcs in main
30 | for func_name in ALL_FUNCS:
31 | imported_module = importlib.import_module("acutebot.funcs." + func_name)
32 |
33 |
34 | class Starter:
35 | def __init__(self, name):
36 | self.photo = "https://telegra.ph/file/8109bf8f6b27ce9b45ff1.jpg"
37 | self.text = st.START_STRING.format(name)
38 | self.reply_markup = InlineKeyboardMarkup(
39 | [
40 | [
41 | InlineKeyboardButton(
42 | text="Movie", switch_inline_query_current_chat=" ",
43 | ),
44 | InlineKeyboardButton(
45 | text="TVshow", switch_inline_query_current_chat=" ",
46 | ),
47 | InlineKeyboardButton(
48 | text="Anime", switch_inline_query_current_chat=" ",
49 | ),
50 | ],
51 | [InlineKeyboardButton(text="Help and Commands❔", callback_data="help")],
52 | ]
53 | )
54 |
55 |
56 | @run_async
57 | def start(update, context):
58 | if update.effective_chat.type == "private":
59 | stuff = Starter(update.effective_user.first_name)
60 | return update.effective_message.reply_photo(
61 | photo=stuff.photo, caption=stuff.text, reply_markup=stuff.reply_markup
62 | )
63 |
64 | update.effective_message.reply_text(st.START_STRING_GRP)
65 |
66 |
67 | @run_async
68 | def help_button(update, context=None):
69 | query = update.callback_query
70 | query.answer()
71 | query.message.edit_caption(
72 | caption=st.HELP_STR,
73 | reply_markup=InlineKeyboardMarkup(
74 | [
75 | [
76 | InlineKeyboardButton(text="Movies & TV", callback_data="h_mv"),
77 | InlineKeyboardButton(
78 | text="Music & lyrics", callback_data="h_music"
79 | ),
80 | ],
81 | [
82 | InlineKeyboardButton(text="Anime & manga", callback_data="h_anime"),
83 | InlineKeyboardButton(text="Miscellaneous", callback_data="h_misc"),
84 | ],
85 | [
86 | InlineKeyboardButton(
87 | text="🖤 About and donate 🖤", callback_data="h_about"
88 | )
89 | ],
90 | [InlineKeyboardButton(text="Go back 🔙", callback_data="back_btn")],
91 | ]
92 | ),
93 | )
94 |
95 |
96 | def h_for_funcs(update, context):
97 | query = update.callback_query
98 | query.answer()
99 | match = query.data.split("_")[1]
100 | markup = InlineKeyboardMarkup(
101 | [[InlineKeyboardButton(text="Go back 🔙", callback_data="back_btn_help")]]
102 | )
103 | if match == "mv":
104 | query.message.edit_caption(caption=st.MOVIE_HELP, reply_markup=markup)
105 | elif match == "music":
106 | query.message.edit_caption(caption=st.MUSIC_HELP, reply_markup=markup)
107 | elif match == "anime":
108 | query.message.edit_caption(caption=st.ANIME_HELP, reply_markup=markup)
109 | elif match == "misc":
110 | query.message.edit_caption(caption=st.MISC_HELP, reply_markup=markup)
111 | elif match == "about":
112 | query.message.edit_caption(
113 | caption=st.ABOUT_STR,
114 | reply_markup=InlineKeyboardMarkup(
115 | [
116 | [
117 | InlineKeyboardButton(
118 | text="Github 🔭", url="https://github.com/starry69"
119 | ),
120 | InlineKeyboardButton(
121 | text="Donate 🖤", url="paypal.me/starryrays"
122 | ),
123 | ],
124 | [
125 | InlineKeyboardButton(
126 | text="Go back 🔙", callback_data="back_btn_help"
127 | )
128 | ],
129 | ]
130 | ),
131 | )
132 |
133 |
134 | @run_async
135 | def back_btn(update, context):
136 | query = update.callback_query
137 | query.answer()
138 | match = query.data.split("_")
139 | if "help" in match:
140 | return help_button(update)
141 | stuff = Starter(update.effective_user.first_name)
142 | query.message.edit_caption(caption=stuff.text, reply_markup=stuff.reply_markup)
143 |
144 |
145 | BANNER = r"""
146 | ___ ___ ______ ___
147 | / _ \ | | | ___ \ | |
148 | / /_\ \ ___ _ _| |_ ___| |_/ / ___ | |_
149 | | _ |/ __| | | | __/ _ \ ___ \/ _ \| __|
150 | | | | | (__| |_| | || __/ |_/ / (_) | |_
151 | \_| |_/\___|\__,_|\__\___\____/ \___/ \__|
152 |
153 | Is Running 🎶🎶🎵
154 | """
155 |
156 |
157 | def main():
158 | def stop_and_restart():
159 | updater.stop()
160 | os.execl(sys.executable, sys.executable, *sys.argv)
161 |
162 | def restart(update, context):
163 | context.bot.sendMessage(update.effective_chat.id, "Rebooted ✨")
164 | Thread(target=stop_and_restart).start()
165 |
166 | restart_handler = CommandHandler("reboot", restart, filters=Filters.user(DEV_ID))
167 | start_handler = CommandHandler("start", start)
168 | help_funcs_handler = CallbackQueryHandler(h_for_funcs, pattern=r"h_")
169 | help_handler = CallbackQueryHandler(help_button, pattern=r"help")
170 | back_btn_handler = CallbackQueryHandler(back_btn, pattern=r"back_btn")
171 |
172 | dp.add_handler(restart_handler)
173 | dp.add_handler(start_handler)
174 | dp.add_handler(help_funcs_handler)
175 | dp.add_handler(help_handler)
176 | dp.add_handler(back_btn_handler)
177 |
178 | LOG.info("%s", BANNER)
179 |
180 | # Start the bot.
181 | updater.start_polling(timeout=15, read_latency=4)
182 | updater.idle()
183 |
184 |
185 | if __name__ == "__main__":
186 | main()
187 |
--------------------------------------------------------------------------------
/acutebot/funcs/inlinequery.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 | import requests as r
16 |
17 | from telegram.ext import InlineQueryHandler
18 | from telegram.ext.dispatcher import run_async
19 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton
20 |
21 | from acutebot import dp, TMDBAPI
22 | from acutebot.helpers import strings as st
23 | from acutebot.helpers.parsedata import article
24 | from acutebot.helpers.keyboard import keyboard
25 | from acutebot.helpers.database import users_sql as sql
26 |
27 |
28 | pic_url = "https://image.tmdb.org/t/p"
29 | base_url = "https://api.themoviedb.org/3"
30 | anime_url = "https://kitsu.io/api/edge"
31 |
32 |
33 | @run_async
34 | def inlinequery(update, context):
35 | query = update.inline_query.query
36 | sql.update_user(
37 | update.inline_query.from_user.id, update.inline_query.from_user.username
38 | )
39 | results = []
40 | if len(query) > 0:
41 | if query.startswith(""):
42 | query = query.replace("", "")
43 | res = r.get(
44 | f"{base_url}/search/tv?api_key={TMDBAPI}"
45 | + f"&language=en&query={query}"
46 | + "&page=1&include_adult=true"
47 | )
48 |
49 | if res.status_code != 200:
50 | return
51 | res = res.json()
52 |
53 | if len(res["results"]) > 0:
54 | for con in res["results"]:
55 | results.append(
56 | article(
57 | title=con.get("name", "N/A"),
58 | description=con.get("first_air_date", "N/A"),
59 | thumb_url=f"{pic_url}/w500/{con['poster_path']}",
60 | message_text=st.INLINE_STR.format(
61 | con.get("original_name", "N/A"),
62 | con.get("first_air_date", "N/A"),
63 | con.get("popularity", "N/A"),
64 | con.get("original_language", "N/A"),
65 | con.get("vote_average", "N/A"),
66 | con.get("overview", "N/A"),
67 | )
68 | + f"",
69 | reply_markup=InlineKeyboardMarkup(
70 | keyboard(title=con["original_name"], tv_id=con["id"])
71 | ),
72 | )
73 | )
74 |
75 | elif query.startswith(""):
76 | query = query.replace("", "")
77 | res = r.get(
78 | f"{base_url}/search/movie?api_key={TMDBAPI}"
79 | + f"&language=en&query={query}"
80 | + "&page=1&include_adult=true"
81 | )
82 |
83 | if res.status_code != 200:
84 | return
85 | res = res.json()
86 |
87 | if len(res["results"]) > 0:
88 | for con in res["results"]:
89 | results.append(
90 | article(
91 | title=con.get("title", "N/A"),
92 | description=con.get("release_date", "N/A"),
93 | thumb_url=f"{pic_url}/w500/{con['poster_path']}",
94 | message_text=st.INLINE_STR.format(
95 | con.get("original_title", "N/A"),
96 | con.get("release_date", "N/A"),
97 | con.get("popularity", "N/A"),
98 | con.get("original_language", "N/A"),
99 | con.get("vote_average", "N/A"),
100 | con.get("overview", "N/A"),
101 | )
102 | + f"",
103 | reply_markup=InlineKeyboardMarkup(
104 | keyboard(title=con["original_title"], mv_id=con["id"])
105 | ),
106 | )
107 | )
108 |
109 | elif query.startswith(""):
110 | query = query.replace("", "")
111 | res = r.get(f"{anime_url}/anime?filter%5Btext%5D={query}")
112 | if res.status_code != 200:
113 | return
114 | res = res.json()["data"]
115 |
116 | for con in res:
117 | data = con["attributes"]
118 | results.append(
119 | article(
120 | title=data["titles"].get("en", "NA"),
121 | description=data["titles"].get("ja_jp", ""),
122 | thumb_url=data["posterImage"]["original"],
123 | message_text=st.ANIME_STR.format(
124 | data["titles"].get("en", ""),
125 | data["titles"].get("ja_jp", ""),
126 | data.get("subtype", "N/A"),
127 | data.get("ageRatingGuide", "N/A"),
128 | data.get("averageRating", "N/A"),
129 | data.get("status", "N/A"),
130 | data.get("startDate", "N/A"),
131 | data.get("endDate", "N/A"),
132 | data.get("episodeLength", "N/A"),
133 | data.get("episodeCount", "N/A"),
134 | data.get("synopsis", "N/A"),
135 | )
136 | + f"",
137 | reply_markup=InlineKeyboardMarkup(
138 | keyboard(
139 | title=data["titles"].get("en"),
140 | anime_ytkey=data.get("youtubeVideoId"),
141 | anime_id=con["id"],
142 | )
143 | ),
144 | )
145 | )
146 |
147 | else:
148 | results.append(
149 | article(
150 | title="Usage: | | ",
151 | description="Example: Avengers endgame",
152 | message_text=st.INLINE_DESC,
153 | thumb_url="https://telegra.ph/file/8109bf8f6b27ce9b45ff1.jpg",
154 | reply_markup=InlineKeyboardMarkup(
155 | [
156 | [
157 | InlineKeyboardButton(
158 | text="Movies",
159 | switch_inline_query_current_chat=" ",
160 | ),
161 | InlineKeyboardButton(
162 | text="TVshows",
163 | switch_inline_query_current_chat=" ",
164 | ),
165 | InlineKeyboardButton(
166 | text="Anime",
167 | switch_inline_query_current_chat=" ",
168 | ),
169 | ]
170 | ]
171 | ),
172 | )
173 | )
174 |
175 | update.inline_query.answer(results[:50], cache_time=10)
176 |
177 |
178 | INLINE_HANDLER = InlineQueryHandler(inlinequery)
179 | dp.add_handler(INLINE_HANDLER)
180 |
--------------------------------------------------------------------------------
/acutebot/helpers/strings.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # MIT License
5 | # Copyright (c) 2020 Stɑrry Shivɑm // This file is part of AcuteBot
6 | #
7 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
10 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
12 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13 | # SOFTWARE.
14 |
15 |
16 | from acutebot import __version__
17 | from platform import python_version
18 | from telegram import __version__ as _libv_
19 |
20 | # Contents
21 | MOVIE_STR = """
22 | ️{} : {}
23 |
24 | • Status : {}
25 | • Genres : {}
26 | • Languages : {}
27 | • Runtime : {} minutes
28 | • Budget : {}
29 | • Revenue : {}
30 | • Release Date : {}
31 | • Average Rating : {}
32 | • Popularity : {}
33 |
34 | • OverView : {}
35 | """
36 |
37 | TV_STR = """
38 | {}
39 |
40 | • Created by : {}
41 | • Genres : {}
42 | • Languages : {}
43 | • Episodes Runtime : {}
44 | • First aired : {}
45 | • Last aired : {}
46 | • Status : {}
47 | • Seasons : {}
48 | • Total EPs : {}
49 | • Average Rating : {}
50 | • Production Company : {}
51 |
52 | • OverView : {}
53 | """
54 |
55 | ANIME_STR = """
56 | {} | {}
57 |
58 | • Category : {}
59 | • Type : {}
60 | • Average Rating : {}
61 | • Status : {}
62 | • First aired : {}
63 | • Last aired : {}
64 | • Runtime : {} minutes
65 | • No of episodes : {}
66 |
67 | • Synopsis : {}
68 | """
69 |
70 | MANGA_STR = """
71 | {} | {}
72 | • Type : {}
73 | • Average Rating : {}
74 | • Status : {}
75 | • First release : {}
76 | • Last release : {}
77 | • Volume count : {}
78 | • No of chapters : {}
79 | • Serialization : {}
80 |
81 | • Synopsis : {}
82 | """
83 |
84 | # Inline Content
85 | INLINE_STR = """
86 | • Title : {}
87 | • Release : {}
88 | • Popularity : {}
89 | • Language : {}
90 | • Average Rating : {}
91 |
92 | • OverView : {}
93 | """
94 |
95 | INLINE_DESC = """
96 | Usage: <tv> title
or <movie> title
in inline query.
97 |
98 | Examples:
99 | × <movie> Avengers Endgame
100 | × <tv> Breaking Bad
101 | × <anime> Attack on Titan
102 | • You can try on buttons below!
103 | """
104 |
105 |
106 | # Start
107 | START_STRING = """
108 | Hey {}, I'm acutebot and i can help you to get \
109 | information about your favorite movies, tv and anime shows, you can also download \
110 | music & can view song lyrics using me! Just click the button \
111 | below to get started with list of possible commands...
112 |
113 | You can also search movies, tvshows & \
114 | anime inline! just type @acutebot
in \
115 | your message box and follow the instructions.
116 |
117 | And don't forget to smile, atleast once in a while ;)
118 | """
119 | START_STRING_GRP = "Hmmm?"
120 |
121 |
122 | # About
123 | ABOUT_STR = f"""
124 | I'm fully written in \
125 | Python3 by starry, \
126 | feel free to report him if you find any rough edge inside me.
127 |
128 | × Bot version : {__version__}
129 | × Python version : {python_version()}
130 | × Library version : PTB {_libv_}
131 | × Movies & TV data : themoviedb.org
132 | × Anime data from : kitsu.io
133 | × Music data from : deezer.com
134 | × Lyrics data from : genius.com
135 |
136 | If you enjoyed using me & wanna support my creator \
137 | hit the donate button below, since he's just a student so \
138 | every little helps to pay for my server, and ofcourse boosting morale ;)
139 |
140 | """
141 |
142 | # Help
143 | HELP_STR = """
144 | Hey there, click on the buttons below to get documentations \
145 | for the related functions.
146 | """
147 |
148 | MOVIE_HELP = """
149 | 🗒️ Documentation for Movies & TV related functions:
150 |
151 | × /movies : Search for info about your favorite movies.
152 | × /tvshows : Get information for your favotite TV shows.
153 | × /toprated : (Soon) | View information about top rated, Movie & TV titles.
154 | """
155 | ANIME_HELP = """
156 | 🗒️ Documentation for Anime & Manga related functionsfunctions:
157 |
158 | × /anime : Search for info about your favorite anime titles.
159 | × /manga : Get information about your favorite manga titles.
160 | """
161 | MUSIC_HELP = """
162 | 🗒️ Documentation for music & lyrics related functions:
163 |
164 | × /music : Download your favorite music in high resolution.
165 | × /lyrics : Get lyrics for your favorite songs.
166 | × /nowplaying : Flex you currently or last played song in spotify.
167 | """
168 | MISC_HELP = """
169 | 🗒️ Documentation for some miscs command which don't fit anywhere!
170 |
171 | × /reddit : Gets you random memes scraped from popular subreddits.
172 | × /subtitle : Download subtitles for your movies.
173 | × /watchlist : Get list of saved shows from your watchlist :D.
174 | """
175 |
176 | # Errors
177 | API_ERR = "Sorry, couldn't reach API at the moment :("
178 | NOT_FOUND = "Sorry, couldn't find any results for the query :("
179 | INVALIDCAT = "Hmmm.. maybe you've sent wrong category to look for, please try again!"
180 | KEYERROR = "Oops! something went wrong. Please try again :("
181 |
182 | # Cancel
183 | CANCEL = "Cancelled the current task!"
184 |
185 | # To search
186 | TOSEARCHMOVIE = "Please reply with the movie title you wanna look for."
187 | TOSEARCHTV = "Please reply with the TV title you wanna look for."
188 | TOSEARCH_ANIME = "Please reply with the anime title you want to look for."
189 | TOSEARCH_MANGA = "Please reply with the manga name you wanna look for."
190 |
191 | # Favs
192 | NOFAVS = "Hmmm 🤔 looks like you don't have any title saved in your watchlist yet!"
193 | REMFAV = "Great work! Successfully cleared your watchlist :)"
194 | SAVED_FAV = "Added '{}' to your Watchlist!"
195 | FAV_EXIST = (
196 | "Hey there this title is already in your watchlist, Go & finish it instead ;)"
197 | )
198 | NOT_ALLOWED = "The one who issued the command shall only click this holy button."
199 |
200 | # Stats
201 | STATS = """
202 | 📊 Current Stats;
203 | 👥 Total users : {}
204 | 💛 Watchlist saved : {}
205 | """
206 |
207 | # Greet
208 | GREET = "Hey {}! Thank you for adding me in {} :)"
209 |
210 | # Lyrics
211 | SONGNAME = "Please tell me name of the song you want lyrics for."
212 | ARTISTNAME = "Great! now tell me name of the artist for this song."
213 |
214 | LYRICS_ERR = """Sorry, looks like i forgot your song name, possibly due to restart \
215 | Would you mind sending me again?
216 | """
217 | LYRIC_NOT_FOUND = "Sorry i couldn't find lyrics for that song."
218 | LYRICS_TOO_BIG = (
219 | "Lyrics of this song is too big for telegram, I'm sending it as a file..."
220 | )
221 |
222 | # Music
223 | MUSICQ = "Please choose the quality of music :)"
224 | MUSICNAME = "Okay! tell me name of the song you're looking for."
225 | UPLOAD_BOTAPI = "⌛ uploading song please wait..."
226 | UPLOAD_MTPROTO = "Hmm, file size is more than 50MBs, uploading via mtproto this might take around 5 mins, please wait..."
227 | MUSICNOTFOUND = "Sorry i couldn't find that song :("
228 |
229 | # Subtitles
230 | TOSEARCHSUBS = "Please reply with the Movie | Anime name you want subs for."
231 | SUBS_STR = "🏷 Subtitles for {}.\nClick on buttons below to download!"
232 |
233 | # Spotify
234 | SPT_LOGIN = "Hey {}, Please click the button below to login with your spotify account."
235 | SPT_LOGIN_PM = "Please contact me in PM to login with your spotify account, inorder to use this feature."
236 |
--------------------------------------------------------------------------------