Initial commit
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/dist/
|
||||||
|
/build/
|
||||||
|
/venv/
|
||||||
|
config.storage
|
||||||
|
*.session
|
||||||
5
README.md
Normal file
5
README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Bars's Session Keeper
|
||||||
|
Simple telegram session generator and keeper, that can get activation code.
|
||||||
|
|
||||||
|
## Tip
|
||||||
|
Move build of this app and .session files on encrypted flash drive to keep them safe.
|
||||||
6
TelegramSessionKeeper.py
Normal file
6
TelegramSessionKeeper.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from modules.menu import menu
|
||||||
|
from modules.menu.fill import fill_menu
|
||||||
|
|
||||||
|
|
||||||
|
fill_menu()
|
||||||
|
menu.show()
|
||||||
44
TelegramSessionKeeper.spec
Normal file
44
TelegramSessionKeeper.spec
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['TelegramSessionKeeper.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='TelegramSessionKeeper',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
0
modules/__init__.py
Normal file
0
modules/__init__.py
Normal file
1
modules/client/__init__.py
Normal file
1
modules/client/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .client import GeneratedClient
|
||||||
19
modules/client/client.py
Normal file
19
modules/client/client.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from ..config import config
|
||||||
|
from pyrogram import Client
|
||||||
|
|
||||||
|
|
||||||
|
class GeneratedClient(Client):
|
||||||
|
def __init__(self, name: str):
|
||||||
|
if not config.get('api_id'):
|
||||||
|
config['api_id'] = input('api_id from my.telegram.org: ')
|
||||||
|
if not config.get('api_hash'):
|
||||||
|
config['api_hash'] = input('api_hash from my.telegram.org: ')
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
name=name,
|
||||||
|
api_id=config['api_id'],
|
||||||
|
api_hash=config['api_hash'],
|
||||||
|
device_model=config.get('device_model', 'Session Keeper'),
|
||||||
|
app_version=config.get('app_version', 'DO NOT DEAUTH THIS SESSION'),
|
||||||
|
system_version=config.get('system_version', 'Bars\'s TelegramSessionKeeper')
|
||||||
|
)
|
||||||
1
modules/config/__init__.py
Normal file
1
modules/config/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .db import config, sessions
|
||||||
17
modules/config/db.py
Normal file
17
modules/config/db.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import sqlitedict
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigDatabase(sqlitedict.SqliteDict):
|
||||||
|
def __init__(self, tablename):
|
||||||
|
super().__init__(
|
||||||
|
filename='config.storage',
|
||||||
|
tablename=tablename,
|
||||||
|
encode=json.dumps,
|
||||||
|
decode=json.loads,
|
||||||
|
autocommit=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
config = ConfigDatabase('config')
|
||||||
|
sessions = ConfigDatabase('sessions')
|
||||||
15
modules/config/models.py
Normal file
15
modules/config/models.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SessionConfig:
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.phone} - {self.id} - {self.profile_name} {f"- @{self.username}" if self.username else ""}'
|
||||||
|
|
||||||
|
def json(self):
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
phone: str
|
||||||
|
profile_name: str
|
||||||
|
id: int
|
||||||
|
username: str | None = None
|
||||||
65
modules/custom_items/DynamicSubmenu.py
Normal file
65
modules/custom_items/DynamicSubmenu.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
from cursesmenu.items import MenuItem
|
||||||
|
from cursesmenu.curses_menu import CursesMenu
|
||||||
|
from typing import Callable, Any
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicSubmenuItem(MenuItem):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
text: str,
|
||||||
|
generator: Callable[..., CursesMenu],
|
||||||
|
menu: CursesMenu | None = None,
|
||||||
|
args: list[Any] | None = None,
|
||||||
|
kwargs: dict[Any, Any] | None = None,
|
||||||
|
) -> None:
|
||||||
|
self._args: list[Any] | None = args
|
||||||
|
self._kwargs: dict[Any, Any] | None = kwargs
|
||||||
|
self._generator: Callable[..., CursesMenu] | None = generator
|
||||||
|
self._menu: CursesMenu | None = menu
|
||||||
|
self._submenu: CursesMenu | None = None
|
||||||
|
super().__init__(
|
||||||
|
text=text,
|
||||||
|
menu=menu
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def submenu(self) -> CursesMenu | None:
|
||||||
|
return self._submenu
|
||||||
|
|
||||||
|
@submenu.setter
|
||||||
|
def submenu(self, submenu: CursesMenu | None) -> None:
|
||||||
|
self._submenu = submenu
|
||||||
|
if self._submenu is not None:
|
||||||
|
self._submenu.parent = self._menu
|
||||||
|
|
||||||
|
@property
|
||||||
|
def menu(self) -> CursesMenu | None:
|
||||||
|
return self._menu
|
||||||
|
|
||||||
|
@menu.setter
|
||||||
|
def menu(self, menu: CursesMenu | None) -> None:
|
||||||
|
self._menu = menu
|
||||||
|
if self._submenu is not None:
|
||||||
|
self._submenu.parent = menu
|
||||||
|
|
||||||
|
def set_up(self) -> None:
|
||||||
|
assert self.menu is not None
|
||||||
|
self.menu.pause()
|
||||||
|
self.menu.clear_screen()
|
||||||
|
|
||||||
|
def action(self) -> None:
|
||||||
|
self.submenu = self._generator(*self._args if self._args else [],
|
||||||
|
**self._kwargs if self._kwargs else {})
|
||||||
|
self.submenu.start()
|
||||||
|
|
||||||
|
def clean_up(self) -> None:
|
||||||
|
assert self.menu is not None
|
||||||
|
assert self.submenu is not None
|
||||||
|
self.submenu.join()
|
||||||
|
self.submenu.clear_screen()
|
||||||
|
self.menu.resume()
|
||||||
|
|
||||||
|
def get_return(self) -> Any:
|
||||||
|
if self.submenu is not None:
|
||||||
|
return self.submenu.returned_value
|
||||||
|
return None
|
||||||
1
modules/custom_items/__init__.py
Normal file
1
modules/custom_items/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pass
|
||||||
0
modules/decorators/__init__.py
Normal file
0
modules/decorators/__init__.py
Normal file
20
modules/decorators/callback.py
Normal file
20
modules/decorators/callback.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from modules.menu.menu import menu
|
||||||
|
|
||||||
|
|
||||||
|
def callback(f):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
menu.pause()
|
||||||
|
f(*args, **kwargs)
|
||||||
|
menu.resume()
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def async_callback(f):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
import asyncio
|
||||||
|
menu.pause()
|
||||||
|
asyncio.run(f(*args, **kwargs))
|
||||||
|
menu.resume()
|
||||||
|
|
||||||
|
return wrapper
|
||||||
1
modules/menu/__init__.py
Normal file
1
modules/menu/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .menu import menu
|
||||||
0
modules/menu/callbacks/__init__.py
Normal file
0
modules/menu/callbacks/__init__.py
Normal file
1
modules/menu/callbacks/config/__init__.py
Normal file
1
modules/menu/callbacks/config/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pass
|
||||||
16
modules/menu/callbacks/config/callbacks.py
Normal file
16
modules/menu/callbacks/config/callbacks.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from modules.decorators.callback import callback
|
||||||
|
from modules.config import config
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def edit_config_callback(field: str, comments: str, default: str = None):
|
||||||
|
print(f'Current value is {config.get(field)}')
|
||||||
|
print(f'Default value is {default}. Press Enter to restore it')
|
||||||
|
config[field] = input(f'{field} ({comments}) > ')
|
||||||
|
if config[field] in ['None', 'none']:
|
||||||
|
del config[field]
|
||||||
|
|
||||||
|
if config[field] == '' and default is not None:
|
||||||
|
config[field] = default
|
||||||
|
elif default is None:
|
||||||
|
del config[field]
|
||||||
0
modules/menu/callbacks/create_session/__init__.py
Normal file
0
modules/menu/callbacks/create_session/__init__.py
Normal file
33
modules/menu/callbacks/create_session/callbacks.py
Normal file
33
modules/menu/callbacks/create_session/callbacks.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from sqlite3 import OperationalError
|
||||||
|
from rich import print
|
||||||
|
from modules.decorators.callback import async_callback
|
||||||
|
from modules.client import GeneratedClient
|
||||||
|
from modules.config import sessions
|
||||||
|
from modules.config.models import SessionConfig
|
||||||
|
|
||||||
|
|
||||||
|
@async_callback
|
||||||
|
async def create_session_callback():
|
||||||
|
session_name = input('New session name: ')
|
||||||
|
while session_name in sessions.keys():
|
||||||
|
session_name = input('Session with this name is already saved. Try another name: ')
|
||||||
|
|
||||||
|
try:
|
||||||
|
await (client := GeneratedClient(name=session_name)).start()
|
||||||
|
sessions[session_name] = SessionConfig(
|
||||||
|
id=client.me.id,
|
||||||
|
phone=client.phone_number,
|
||||||
|
profile_name=client.me.first_name + (f' {client.me.last_name}' if client.me.last_name else ''),
|
||||||
|
username=client.me.username
|
||||||
|
).json()
|
||||||
|
print(f'[green]Created[/] session {session_name}...')
|
||||||
|
input()
|
||||||
|
|
||||||
|
except OperationalError:
|
||||||
|
print('[red]Cannot create session file.[/] Try using different name...')
|
||||||
|
input()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[red]Error:[/] {e}...')
|
||||||
|
input()
|
||||||
|
return
|
||||||
1
modules/menu/callbacks/get_code/__init__.py
Normal file
1
modules/menu/callbacks/get_code/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .get_code import get_code_callback
|
||||||
6
modules/menu/callbacks/get_code/get_code.py
Normal file
6
modules/menu/callbacks/get_code/get_code.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from modules.decorators.callback import async_callback
|
||||||
|
|
||||||
|
|
||||||
|
@async_callback
|
||||||
|
async def get_code_callback(session_name: str):
|
||||||
|
...
|
||||||
0
modules/menu/dynamic/__init__.py
Normal file
0
modules/menu/dynamic/__init__.py
Normal file
1
modules/menu/dynamic/get_code/__init__.py
Normal file
1
modules/menu/dynamic/get_code/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .submenu import get_code_submenu_item
|
||||||
22
modules/menu/dynamic/get_code/generator.py
Normal file
22
modules/menu/dynamic/get_code/generator.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import os
|
||||||
|
from cursesmenu import CursesMenu
|
||||||
|
from cursesmenu.items import FunctionItem
|
||||||
|
|
||||||
|
from modules.config import sessions
|
||||||
|
from modules.config.models import SessionConfig
|
||||||
|
from ...callbacks.get_code import get_code_callback
|
||||||
|
|
||||||
|
|
||||||
|
def generate_get_code_menu() -> CursesMenu:
|
||||||
|
submenu = CursesMenu(
|
||||||
|
title='Sessions'
|
||||||
|
)
|
||||||
|
for session_name in sessions.keys():
|
||||||
|
if os.path.isfile(f'{session_name}.session'):
|
||||||
|
submenu.items.append(FunctionItem(
|
||||||
|
f'{session_name} - {SessionConfig(**sessions[session_name])}',
|
||||||
|
function=get_code_callback,
|
||||||
|
args=[session_name]
|
||||||
|
))
|
||||||
|
|
||||||
|
return submenu
|
||||||
15
modules/menu/dynamic/get_code/submenu.py
Normal file
15
modules/menu/dynamic/get_code/submenu.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from ...menu import menu
|
||||||
|
from cursesmenu import CursesMenu
|
||||||
|
from modules.custom_items.DynamicSubmenu import DynamicSubmenuItem
|
||||||
|
from .generator import generate_get_code_menu
|
||||||
|
|
||||||
|
|
||||||
|
get_code_submenu = CursesMenu(
|
||||||
|
title='Sessions'
|
||||||
|
)
|
||||||
|
|
||||||
|
get_code_submenu_item = DynamicSubmenuItem(
|
||||||
|
text='Get confirmation code',
|
||||||
|
generator=generate_get_code_menu,
|
||||||
|
menu=menu
|
||||||
|
)
|
||||||
7
modules/menu/fill.py
Normal file
7
modules/menu/fill.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from .menu import menu
|
||||||
|
|
||||||
|
|
||||||
|
def fill_menu():
|
||||||
|
from .items.main import items_list
|
||||||
|
for item in items_list:
|
||||||
|
menu.items.append(item)
|
||||||
0
modules/menu/items/__init__.py
Normal file
0
modules/menu/items/__init__.py
Normal file
1
modules/menu/items/config/__init__.py
Normal file
1
modules/menu/items/config/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .submenu import config_submenu_item
|
||||||
20
modules/menu/items/config/fields.py
Normal file
20
modules/menu/items/config/fields.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from cursesmenu.items import FunctionItem
|
||||||
|
from ...callbacks.config.callbacks import edit_config_callback
|
||||||
|
|
||||||
|
|
||||||
|
items_list = [
|
||||||
|
FunctionItem('app_id', function=edit_config_callback,
|
||||||
|
args=['app_id', 'Telegram app_id, can be obtained on my.telegram.org']),
|
||||||
|
FunctionItem('api_hash', function=edit_config_callback,
|
||||||
|
args=['api_hash', 'Telegram api_hash, can be obtained on my.telegram.org']),
|
||||||
|
FunctionItem('device_model', function=edit_config_callback,
|
||||||
|
args=['device_model', 'Device model is first line of information about login in devices settings. ',
|
||||||
|
'Session Keeper']),
|
||||||
|
FunctionItem('app_version', function=edit_config_callback,
|
||||||
|
args=['app_version', 'App version is second line of information about login in devices settings.',
|
||||||
|
'DO NOT DEAUTH THIS SESSION']),
|
||||||
|
FunctionItem('system_version', function=edit_config_callback,
|
||||||
|
args=['system_version',
|
||||||
|
'App version is shown in detailed information about login in devices settings. ',
|
||||||
|
'Bars\'s TelegramSessionKeeper']),
|
||||||
|
]
|
||||||
17
modules/menu/items/config/submenu.py
Normal file
17
modules/menu/items/config/submenu.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from ...menu import menu
|
||||||
|
from cursesmenu import CursesMenu
|
||||||
|
from cursesmenu.items import SubmenuItem
|
||||||
|
from .fields import items_list
|
||||||
|
|
||||||
|
|
||||||
|
config_submenu = CursesMenu(
|
||||||
|
title='Config'
|
||||||
|
)
|
||||||
|
for item in items_list:
|
||||||
|
config_submenu.items.append(item)
|
||||||
|
|
||||||
|
config_submenu_item = SubmenuItem(
|
||||||
|
text='Edit config',
|
||||||
|
submenu=config_submenu,
|
||||||
|
menu=menu
|
||||||
|
)
|
||||||
1
modules/menu/items/create_session/__init__.py
Normal file
1
modules/menu/items/create_session/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .fields import create_new_session_item
|
||||||
7
modules/menu/items/create_session/fields.py
Normal file
7
modules/menu/items/create_session/fields.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from cursesmenu.items import FunctionItem
|
||||||
|
from ...callbacks.create_session.callbacks import create_session_callback
|
||||||
|
|
||||||
|
|
||||||
|
create_new_session_item = FunctionItem(
|
||||||
|
'Add new session', function=create_session_callback
|
||||||
|
)
|
||||||
10
modules/menu/items/main.py
Normal file
10
modules/menu/items/main.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from .config import config_submenu_item
|
||||||
|
from .create_session import create_new_session_item
|
||||||
|
from ..dynamic.get_code import get_code_submenu_item
|
||||||
|
|
||||||
|
|
||||||
|
items_list = [
|
||||||
|
create_new_session_item,
|
||||||
|
get_code_submenu_item,
|
||||||
|
config_submenu_item
|
||||||
|
]
|
||||||
6
modules/menu/menu.py
Normal file
6
modules/menu/menu.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import cursesmenu
|
||||||
|
|
||||||
|
|
||||||
|
menu = cursesmenu.CursesMenu(
|
||||||
|
title='SessionKeeper'
|
||||||
|
)
|
||||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
pyrogram
|
||||||
|
tgcrypto
|
||||||
|
sqlitedict
|
||||||
|
curses-menu
|
||||||
|
rich
|
||||||
|
pyinstaller
|
||||||
Reference in New Issue
Block a user