moonlib 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- main.py +789 -0
- moonlib-1.0.0.dist-info/METADATA +11 -0
- moonlib-1.0.0.dist-info/RECORD +6 -0
- moonlib-1.0.0.dist-info/WHEEL +5 -0
- moonlib-1.0.0.dist-info/licenses/LICENCE +1 -0
- moonlib-1.0.0.dist-info/top_level.txt +1 -0
main.py
ADDED
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
__ __ _ _ _
|
|
3
|
+
| \/ | ___ ___ _ __ | | (_) |__
|
|
4
|
+
| |\/| |/ _ \ / _ \| '_ \| | | | '_ \
|
|
5
|
+
| | | | (_) | (_) | | | | |___| | |_) |
|
|
6
|
+
|_| |_|\___/ \___/|_| |_|_____|_|_.__/
|
|
7
|
+
|
|
8
|
+
____ __ __ _ ____ _ _
|
|
9
|
+
/ __ \| \/ | | | _ \| |_ _ __ _(_)_ __ ___
|
|
10
|
+
/ / _` | |\/| | | | |_) | | | | |/ _` | | '_ \/ __|
|
|
11
|
+
| | (_| | | | | |___| __/| | |_| | (_| | | | | \__ \
|
|
12
|
+
\ \__,_|_| |_|_____|_| |_|\__,_|\__, |_|_| |_|___/
|
|
13
|
+
\____/ |___/
|
|
14
|
+
|
|
15
|
+
MoonLib - либа для удобной работы с java классами
|
|
16
|
+
В основном для разрабов и тех кто хочет юзать мои плагины где эта либа нужна
|
|
17
|
+
|
|
18
|
+
Код для своего плагина/либы можно брать, но только с указанием @MLPlugins в __author__
|
|
19
|
+
Например: __author__ = "@username & @MLPlugins"
|
|
20
|
+
При использовании либы в своих плагинах указывать авторство не нужно
|
|
21
|
+
|
|
22
|
+
Также небольшой совет: проверять импорты
|
|
23
|
+
Пример:
|
|
24
|
+
try: from moonlib import Fortune, Browser
|
|
25
|
+
except ImportError:
|
|
26
|
+
# Код для ошибки типа "Требуется установить либу MoonLib для работы плагина"
|
|
27
|
+
# Либо можно юзать fallback с ограниченными функциями
|
|
28
|
+
|
|
29
|
+
Остальное можно почитать в документации: @MLDocumentation
|
|
30
|
+
|
|
31
|
+
Gift dataclass я взял у @binbash_0, и немного отредактировал
|
|
32
|
+
|
|
33
|
+
Если есть вопросы, пиши в лс тгк: @MLPlugins
|
|
34
|
+
Но советую перед этим прочитать документацию чтобы избежать бана за глупый вопрос
|
|
35
|
+
Бан будет за вопросы типа "А как импортировать", "Где тут ваще классы?", и т.п
|
|
36
|
+
|
|
37
|
+
HookRouter класс взял из плагина AntiScam от @binbash_0
|
|
38
|
+
|
|
39
|
+
Некоторый код писал на скорую руку, так что может быть говнокод
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
__id__ = "moonlib"
|
|
43
|
+
__name__ = "MoonLib"
|
|
44
|
+
__description__ = "Мини либа для работы с java классами, и т.д\nВ основном для разрабов плагинов или для тех, кто хочет юзать мои плагины\nДокументация: @MLDocumentation"
|
|
45
|
+
__author__ = "@MLPlugins & @MLDocumentation"
|
|
46
|
+
__version__ = "1.0.1"
|
|
47
|
+
__min_version__ = "12.5.1"
|
|
48
|
+
__priority__ = 1
|
|
49
|
+
|
|
50
|
+
MOONLIB_VERSION = __version__
|
|
51
|
+
|
|
52
|
+
from base_plugin import BasePlugin, HookResult, HookStrategy
|
|
53
|
+
from java import jclass
|
|
54
|
+
from android_utils import (copy_to_clipboard, run_on_ui_thread)
|
|
55
|
+
from android_utils import log as _log
|
|
56
|
+
from client_utils import get_last_fragment, run_on_queue
|
|
57
|
+
from dataclasses import dataclass
|
|
58
|
+
from ui.settings import (
|
|
59
|
+
Header,
|
|
60
|
+
Switch,
|
|
61
|
+
Custom
|
|
62
|
+
)
|
|
63
|
+
from ui.alert import AlertDialogBuilder
|
|
64
|
+
from ui.bulletin import BulletinHelper
|
|
65
|
+
from typing import (
|
|
66
|
+
Any,
|
|
67
|
+
Union,
|
|
68
|
+
List,
|
|
69
|
+
)
|
|
70
|
+
from typing_extensions import deprecated
|
|
71
|
+
from file_utils import (get_cache_dir, list_dir, get_plugins_dir)
|
|
72
|
+
|
|
73
|
+
from decimal import(Decimal, getcontext)
|
|
74
|
+
|
|
75
|
+
getcontext().prec = 2
|
|
76
|
+
|
|
77
|
+
import threading
|
|
78
|
+
import pathlib
|
|
79
|
+
import subprocess
|
|
80
|
+
import requests
|
|
81
|
+
import functools
|
|
82
|
+
import os
|
|
83
|
+
import shutil
|
|
84
|
+
import json
|
|
85
|
+
|
|
86
|
+
def find_class(c: str) -> Any:
|
|
87
|
+
loader = jclass("org.telegram.messenger.ApplicationLoader")
|
|
88
|
+
ctx = loader.applicationContext
|
|
89
|
+
cl = loader.getClassLoader()
|
|
90
|
+
if (
|
|
91
|
+
loader and
|
|
92
|
+
ctx and
|
|
93
|
+
cl
|
|
94
|
+
): return cl.loadClass(c)
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
@deprecated("useless")
|
|
98
|
+
def _get_tg_classes() -> dict:
|
|
99
|
+
tg = {
|
|
100
|
+
"browser.Browser": jclass("org.telegram.messenger.browser.Browser"),
|
|
101
|
+
"ApplicationLoader": jclass("org.telegram.messenger.ApplicationLoader"),
|
|
102
|
+
"tgnet.TLRPC": jclass("org.telegram.tgnet.TLRPC"),
|
|
103
|
+
"AndroidUtilities": jclass("org.telegram.messenger.AndroidUtilities"),
|
|
104
|
+
"LocaleController": jclass("org.telegram.messenger.LocaleController")
|
|
105
|
+
}
|
|
106
|
+
return tg
|
|
107
|
+
|
|
108
|
+
@deprecated("useless")
|
|
109
|
+
def _get_java_classes() -> dict:
|
|
110
|
+
java = {
|
|
111
|
+
"util.ArrayList": jclass("java.util.ArrayList"),
|
|
112
|
+
"lang.String": jclass("java.lang.String"),
|
|
113
|
+
"security.SecureRandom": jclass("java.security.SecureRandom"),
|
|
114
|
+
"util.Locale": jclass("java.util.Locale")
|
|
115
|
+
}
|
|
116
|
+
return java
|
|
117
|
+
|
|
118
|
+
@deprecated("useless")
|
|
119
|
+
def _get_android_classes() -> dict:
|
|
120
|
+
android = {
|
|
121
|
+
"os.VibrationEffect": jclass("android.os.VibrationEffect"),
|
|
122
|
+
"os.Process": jclass("android.os.Process"),
|
|
123
|
+
"widget.TextView": jclass("android.widget.TextView"),
|
|
124
|
+
"widget.LinearLayout": jclass("android.widget.LinearLayout"),
|
|
125
|
+
"media.AudioManager": jclass("android.media.AudioManager"),
|
|
126
|
+
"media.ToneGenerator": jclass("android.media.ToneGenerator"),
|
|
127
|
+
"view.WindowManager": jclass("android.view.WindowManager"),
|
|
128
|
+
"webkit.WebView": jclass("android.webkit.WebView"),
|
|
129
|
+
"webkit.WebViewClient": jclass("android.webkit.WebViewClient")
|
|
130
|
+
}
|
|
131
|
+
return android
|
|
132
|
+
|
|
133
|
+
@deprecated("useless")
|
|
134
|
+
def _err(msg: str):
|
|
135
|
+
android = _get_android_classes()
|
|
136
|
+
process = android["os.Process"]
|
|
137
|
+
pid = process.myPid()
|
|
138
|
+
copy_to_clipboard(msg)
|
|
139
|
+
process.killProcess(pid)
|
|
140
|
+
|
|
141
|
+
@deprecated("useless")
|
|
142
|
+
def err(msg: str):
|
|
143
|
+
if msg: _err(msg)
|
|
144
|
+
|
|
145
|
+
def _xor(_str: str, key: int) -> str:
|
|
146
|
+
xor_str = ""
|
|
147
|
+
for char in _str: xor_str += "".join(chr(ord(char) ^ key))
|
|
148
|
+
return xor_str
|
|
149
|
+
|
|
150
|
+
@deprecated("useless")
|
|
151
|
+
def _get_ctx() -> Any:
|
|
152
|
+
tg = _get_tg_classes()
|
|
153
|
+
loader = tg["ApplicationLoader"]
|
|
154
|
+
ctx = loader.applicationContext
|
|
155
|
+
return ctx
|
|
156
|
+
|
|
157
|
+
def _settings_label(text: str) -> Any:
|
|
158
|
+
ctx = _get_ctx()
|
|
159
|
+
android = _get_android_classes()
|
|
160
|
+
_text_view = android["widget.TextView"]
|
|
161
|
+
textView = _text_view(ctx)
|
|
162
|
+
textView.setText(text)
|
|
163
|
+
_label = Custom(view=textView)
|
|
164
|
+
return _label
|
|
165
|
+
|
|
166
|
+
def _find_su() -> str:
|
|
167
|
+
su_dir = [
|
|
168
|
+
"/bin",
|
|
169
|
+
"/sbin",
|
|
170
|
+
"/system/bin",
|
|
171
|
+
"/vendor/bin"
|
|
172
|
+
]
|
|
173
|
+
_su_dir = ""
|
|
174
|
+
for _dir in su_dir:
|
|
175
|
+
file = pathlib.Path(f"{_dir}/su")
|
|
176
|
+
if file.exists(): _su_dir = f"{_dir}/su"
|
|
177
|
+
return _su_dir
|
|
178
|
+
|
|
179
|
+
def _create_text(text: list) -> str:
|
|
180
|
+
_text = ""
|
|
181
|
+
for line in text:
|
|
182
|
+
_text += line
|
|
183
|
+
index = text.index(_text)
|
|
184
|
+
length = text.__len__()
|
|
185
|
+
if length is not index: _text += "\n"
|
|
186
|
+
return _text
|
|
187
|
+
|
|
188
|
+
@dataclass
|
|
189
|
+
class Point:
|
|
190
|
+
x: int
|
|
191
|
+
y: int
|
|
192
|
+
|
|
193
|
+
@dataclass
|
|
194
|
+
class Size:
|
|
195
|
+
width: int
|
|
196
|
+
height: int
|
|
197
|
+
|
|
198
|
+
@dataclass
|
|
199
|
+
class Output:
|
|
200
|
+
stdout: str
|
|
201
|
+
stderr: str
|
|
202
|
+
|
|
203
|
+
@dataclass
|
|
204
|
+
class Gift:
|
|
205
|
+
_id: int
|
|
206
|
+
price: int
|
|
207
|
+
sticker: int
|
|
208
|
+
|
|
209
|
+
@deprecated("TaskManager дает больше контроля и ленивый запуск, в отличии от PartRun")
|
|
210
|
+
class PartRun:
|
|
211
|
+
def __init__(self, init, start, main, _continue, end):
|
|
212
|
+
self.init = init
|
|
213
|
+
self.start = start
|
|
214
|
+
self.main = main
|
|
215
|
+
self._continue = _continue
|
|
216
|
+
self.end = end
|
|
217
|
+
self.init()
|
|
218
|
+
self.start()
|
|
219
|
+
self.main()
|
|
220
|
+
self._continue()
|
|
221
|
+
self.end()
|
|
222
|
+
|
|
223
|
+
# Хз зачем, пусть будет...
|
|
224
|
+
# Все таки помечу как deprecated
|
|
225
|
+
@deprecated("TaskManager дает больше контроля и ленивый запуск, в отличии от LoadClass")
|
|
226
|
+
class LoadClass:
|
|
227
|
+
def __init__(self, load, unload):
|
|
228
|
+
self.load = load
|
|
229
|
+
self.unload = unload
|
|
230
|
+
def _load(self): self.load()
|
|
231
|
+
def _unload(self): self.unload()
|
|
232
|
+
|
|
233
|
+
"""
|
|
234
|
+
Использование TaskManager:
|
|
235
|
+
manager = TaskManager()
|
|
236
|
+
|
|
237
|
+
@manager.Task("foo")
|
|
238
|
+
def foo(): print("bar")
|
|
239
|
+
|
|
240
|
+
@manager.Task("bar")
|
|
241
|
+
def bar(): print("foo")
|
|
242
|
+
|
|
243
|
+
manager.Run("foo")
|
|
244
|
+
manager.Run("bar")
|
|
245
|
+
|
|
246
|
+
# Тута запуск задач по порядку создания
|
|
247
|
+
manager.RunAll()
|
|
248
|
+
"""
|
|
249
|
+
class TaskManager:
|
|
250
|
+
def __init__(self): self.tasks = {}
|
|
251
|
+
def Task(self, name: str):
|
|
252
|
+
def decorator(function):
|
|
253
|
+
self.tasks[name] = {"function": function}
|
|
254
|
+
return function
|
|
255
|
+
return decorator
|
|
256
|
+
def Run(self, name: str):
|
|
257
|
+
task = self.tasks.get(name)
|
|
258
|
+
if task:
|
|
259
|
+
function = task.get("function")
|
|
260
|
+
if function: function()
|
|
261
|
+
def RunAll(self):
|
|
262
|
+
for name in self.tasks:
|
|
263
|
+
task = self.tasks.get(name)
|
|
264
|
+
if task:
|
|
265
|
+
function = task.get("function")
|
|
266
|
+
if function: function()
|
|
267
|
+
|
|
268
|
+
# В отличии от класса в других моих плагинах, тута я поменял получение сервиса, должно работать везде
|
|
269
|
+
class VibBlink:
|
|
270
|
+
def vib(self):
|
|
271
|
+
ctx = _get_ctx()
|
|
272
|
+
android = _get_android_classes()
|
|
273
|
+
vibration = ctx.getSystemService(ctx.VIBRATOR_SERVICE)
|
|
274
|
+
if vibration:
|
|
275
|
+
try:
|
|
276
|
+
effect = android["os.VibrationEffect"]
|
|
277
|
+
vibration.vibrate(effect.createOneShot(1000, 500))
|
|
278
|
+
except:
|
|
279
|
+
vibration.vibrate(1000)
|
|
280
|
+
def blink(self):
|
|
281
|
+
thread = threading.Thread(target=self.vib, daemon=True)
|
|
282
|
+
thread.start()
|
|
283
|
+
|
|
284
|
+
class File:
|
|
285
|
+
@staticmethod
|
|
286
|
+
def Read(path: str, encoding: str) -> str:
|
|
287
|
+
with open(path, "r", encoding=encoding) as file:
|
|
288
|
+
data = file.read()
|
|
289
|
+
return data
|
|
290
|
+
@staticmethod
|
|
291
|
+
def Write(path: str, encoding: str, data: str):
|
|
292
|
+
with open(path, "w", encoding=encoding) as file:
|
|
293
|
+
file.write(data)
|
|
294
|
+
@staticmethod
|
|
295
|
+
def Exists(path: str) -> bool:
|
|
296
|
+
file = pathlib.Path(path)
|
|
297
|
+
return file.exists()
|
|
298
|
+
@staticmethod
|
|
299
|
+
def ReadLine(path: str, encoding: str) -> Any:
|
|
300
|
+
with open(path, "r", encoding=encoding) as file:
|
|
301
|
+
for line in file.readlines(): yield line
|
|
302
|
+
|
|
303
|
+
class TelegramBrowser:
|
|
304
|
+
browser = None
|
|
305
|
+
context = None
|
|
306
|
+
|
|
307
|
+
def OpenUrl(self, url: str):
|
|
308
|
+
tg = _get_tg_classes()
|
|
309
|
+
self.browser = tg["browser.Browser"]
|
|
310
|
+
self.context = _get_ctx()
|
|
311
|
+
self.browser.openUrl(self.context, url)
|
|
312
|
+
|
|
313
|
+
class FileDownload:
|
|
314
|
+
@staticmethod
|
|
315
|
+
def Download(url: str, path: str):
|
|
316
|
+
response = requests.get(url, timeout=30)
|
|
317
|
+
response.raise_for_status()
|
|
318
|
+
|
|
319
|
+
with open(path, "wb") as file:
|
|
320
|
+
file.write(response.content)
|
|
321
|
+
|
|
322
|
+
def _get_args(text: str) -> list:
|
|
323
|
+
args = text.split(" ")
|
|
324
|
+
return args
|
|
325
|
+
|
|
326
|
+
def _get_args_str(text: str) -> str:
|
|
327
|
+
space_index = text.find(" ")
|
|
328
|
+
if space_index is -1: return ""
|
|
329
|
+
args = text[space_index + 1:].strip()
|
|
330
|
+
return args
|
|
331
|
+
|
|
332
|
+
def log(prefix: str, msg: str):
|
|
333
|
+
_str = f"[{prefix}] {msg}"
|
|
334
|
+
_log(_str)
|
|
335
|
+
|
|
336
|
+
# Не юзайте там, где нужны esc codes, zero, ZWNJ, BOM, и т.д
|
|
337
|
+
def _clear_str(_str: str) -> str:
|
|
338
|
+
string = _str.replace("\0", "") # Zero
|
|
339
|
+
string = string.replace("\x00", "") # Zero
|
|
340
|
+
string = string.replace("\a", "") # Beep
|
|
341
|
+
string = string.replace("\u200C", "") # ZWNJ
|
|
342
|
+
string = string.replace("\r", "") # Это уже конвертация DOS в UNIX, но похуй
|
|
343
|
+
string = string.replace("\b", "") # Backspace
|
|
344
|
+
string = string.replace("\xEF\xBB\xBF", "") # BOM
|
|
345
|
+
return string
|
|
346
|
+
|
|
347
|
+
class Fortune:
|
|
348
|
+
def parse_fortunes(self, path: str) -> list:
|
|
349
|
+
file = File()
|
|
350
|
+
data = file.Read(path, "UTF-8")
|
|
351
|
+
fortunes = self.parse_fortunes_with_data(data)
|
|
352
|
+
return fortunes
|
|
353
|
+
def parse_fortunes_with_data(self, data: str) -> list:
|
|
354
|
+
fortunes = []
|
|
355
|
+
lines = []
|
|
356
|
+
_data = _clear_str(data)
|
|
357
|
+
|
|
358
|
+
for line in _data.splitlines():
|
|
359
|
+
if line.strip() is "%":
|
|
360
|
+
if lines:
|
|
361
|
+
fortune = "\n".join(lines).strip()
|
|
362
|
+
fortunes.append(fortune)
|
|
363
|
+
lines = []
|
|
364
|
+
else: lines.append(line)
|
|
365
|
+
return fortunes
|
|
366
|
+
|
|
367
|
+
@deprecated("useless")
|
|
368
|
+
def _get_activity() -> Any:
|
|
369
|
+
ctx = _get_ctx()
|
|
370
|
+
fragment = ctx.get("fragment") or get_last_fragment()
|
|
371
|
+
activity = fragment.getParentActivity()
|
|
372
|
+
return activity
|
|
373
|
+
|
|
374
|
+
@deprecated("not working")
|
|
375
|
+
def _alert_dialog(title: str, msg: str, positive_str: str, negative_str: str, on_positive_click, on_negative_click):
|
|
376
|
+
activity = _get_activity()
|
|
377
|
+
builder = AlertDialogBuilder(activity)
|
|
378
|
+
builder.set_title(title)
|
|
379
|
+
builder.set_message(msg)
|
|
380
|
+
builder.set_positive_button(positive_str, on_positive_click)
|
|
381
|
+
builder.set_negative_button(negative_str, on_negative_click)
|
|
382
|
+
builder.create()
|
|
383
|
+
builder.set_cancelable(True)
|
|
384
|
+
builder.show()
|
|
385
|
+
|
|
386
|
+
class Subprocess:
|
|
387
|
+
@staticmethod
|
|
388
|
+
def Run(args: list) -> Output:
|
|
389
|
+
output = Output(stdout="", stderr="")
|
|
390
|
+
if args:
|
|
391
|
+
process = subprocess.run(args, text=True, capture_output=True)
|
|
392
|
+
output = Output(stdout=process.stdout, stderr=process.stderr)
|
|
393
|
+
return output
|
|
394
|
+
@staticmethod
|
|
395
|
+
def ShellRun(cmd: str) -> Output:
|
|
396
|
+
output = Output(stdout="", stderr="")
|
|
397
|
+
if cmd:
|
|
398
|
+
process = subprocess.run(cmd, text=True, capture_output=True, shell=True)
|
|
399
|
+
output = Output(stdout=process.stdout, stderr=process.stderr)
|
|
400
|
+
return output
|
|
401
|
+
|
|
402
|
+
@dataclass
|
|
403
|
+
class Command:
|
|
404
|
+
name: str
|
|
405
|
+
command: str
|
|
406
|
+
prefix: str
|
|
407
|
+
function: Any
|
|
408
|
+
|
|
409
|
+
class PhysicsUtils:
|
|
410
|
+
CELSIUS = 0x0000
|
|
411
|
+
KELVIN = 0x1FFF
|
|
412
|
+
|
|
413
|
+
def __init__(self):
|
|
414
|
+
getcontext().prec = 2
|
|
415
|
+
|
|
416
|
+
# address - адрес к единице измерения в которую нужно конвертировать
|
|
417
|
+
# В классе есть константы с адресами
|
|
418
|
+
def convert(self, address: int, value: Decimal) -> Decimal:
|
|
419
|
+
_value: Decimal = value
|
|
420
|
+
match address:
|
|
421
|
+
case self.CELSIUS: _value -= Decimal("273.15")
|
|
422
|
+
case self.KELVIN: _value += Decimal("273.15")
|
|
423
|
+
return _value
|
|
424
|
+
|
|
425
|
+
# HookRouter взял из плагина AntiScam от @binbash_0
|
|
426
|
+
class HookRouter:
|
|
427
|
+
def __init__(self):
|
|
428
|
+
self.handlers = {}
|
|
429
|
+
|
|
430
|
+
def hook(self, name: Union[str, List[str]]):
|
|
431
|
+
names = frozenset([name] if isinstance(name, str) else name)
|
|
432
|
+
|
|
433
|
+
def decorator(func):
|
|
434
|
+
@functools.wraps(func)
|
|
435
|
+
def wrapper(calling_name, *args, **kwargs):
|
|
436
|
+
if calling_name not in names:
|
|
437
|
+
return HookResult(strategy=HookStrategy.DEFAULT)
|
|
438
|
+
return func(*args, **kwargs)
|
|
439
|
+
|
|
440
|
+
for n in names:
|
|
441
|
+
self.handlers.setdefault(n, []).append(wrapper)
|
|
442
|
+
return func
|
|
443
|
+
|
|
444
|
+
return decorator
|
|
445
|
+
|
|
446
|
+
def _stop_plugin(_id: str):
|
|
447
|
+
def stop():
|
|
448
|
+
PluginsController = find_class("com.exteragram.messenger.plugins.PluginsController")
|
|
449
|
+
instance = PluginsController.getInstance()
|
|
450
|
+
plugin = instance.plugins.get(_id)
|
|
451
|
+
plugin.setEnabled(False)
|
|
452
|
+
run_on_queue(stop, delay=50)
|
|
453
|
+
|
|
454
|
+
class JavaExceptions:
|
|
455
|
+
def __init__(self):
|
|
456
|
+
self.JavaException = jclass("java.lang.Exception")
|
|
457
|
+
self.RuntimeException = jclass("java.lang.RuntimeException")
|
|
458
|
+
self.SecurityException = jclass("java.lang.SecurityException")
|
|
459
|
+
self.IllegalStateException = jclass("java.lang.IllegalStateException")
|
|
460
|
+
self.NullPointerException = jclass("java.lang.NullPointerException")
|
|
461
|
+
self.AccessViolationException = jclass("java.lang.AccessViolationException")
|
|
462
|
+
self.IllegalArgumentException = jclass("java.lang.IllegalArgumentException")
|
|
463
|
+
self.IOException = jclass("java.lang.IOException")
|
|
464
|
+
self.SocketException = jclass("java.lang.SocketException")
|
|
465
|
+
self.ConnectException = jclass("java.lang.ConnectException")
|
|
466
|
+
not_exceptions_1 = not self.JavaException and not self.RuntimeException
|
|
467
|
+
not_exceptions_2 = not self.SecurityException and not self.IllegalStateException
|
|
468
|
+
not_exceptions_3 = not self.NullPointerException and not self.AccessViolationException
|
|
469
|
+
not_exceptions_4 = not self.IllegalArgumentException and not self.IOException
|
|
470
|
+
not_exceptions_5 = not self.SocketException and not self.ConnectException
|
|
471
|
+
not_exceptions = (
|
|
472
|
+
not_exceptions_1 and
|
|
473
|
+
not_exceptions_2 and
|
|
474
|
+
not_exceptions_3 and
|
|
475
|
+
not_exceptions_4 and
|
|
476
|
+
not_exceptions_5
|
|
477
|
+
)
|
|
478
|
+
if (not_exceptions): _stop_plugin(__id__)
|
|
479
|
+
self.exceptions = {"java.lang.Exception": self.JavaException}
|
|
480
|
+
self.exceptions["java.lang.RuntimeException"], self.exceptions["java.lang.SecurityException"], self.exceptions["java.lang.IllegalStateException"], self.exceptions["java.lang.NullPointerException"], self.exceptions["java.lang.AccessViolationException"], self.exceptions["java.lang.IllegalArgumentException"], self.exceptions["java.lang.IOException"], self.exceptions["java.lang.SocketException"], self.exceptions["java.lang.ConnectException"] = (
|
|
481
|
+
self.RuntimeException,
|
|
482
|
+
self.SecurityException,
|
|
483
|
+
self.IllegalStateException,
|
|
484
|
+
self.NullPointerException,
|
|
485
|
+
self.AccessViolationException,
|
|
486
|
+
self.IllegalArgumentException,
|
|
487
|
+
self.IOException,
|
|
488
|
+
self.SocketException,
|
|
489
|
+
self.ConnectException
|
|
490
|
+
)
|
|
491
|
+
def _java_raise(self, exception: str):
|
|
492
|
+
_exception = self.exceptions.get(exception)
|
|
493
|
+
if _exception: raise _exception()
|
|
494
|
+
def _get_exception(self, exception: str) -> Any:
|
|
495
|
+
_exception = self.exceptions.get(exception)
|
|
496
|
+
return _exception
|
|
497
|
+
|
|
498
|
+
class AdvancedBulletinHelper(BulletinHelper):
|
|
499
|
+
def __init__(self):
|
|
500
|
+
self.INFO = 0x1000
|
|
501
|
+
self.ERROR = 0xFFFF
|
|
502
|
+
self.SUCCESS = 0x0000
|
|
503
|
+
# show функция только для info, error, success
|
|
504
|
+
# Для button юзаем show_with_button из ориг BulletinHelper
|
|
505
|
+
def show(self, msg: str, status: int):
|
|
506
|
+
bulletin = BulletinHelper.show_info # Будет show_info как fallback
|
|
507
|
+
match status:
|
|
508
|
+
case self.ERROR: bulletin = BulletinHelper.show_error
|
|
509
|
+
case self.SUCCESS: bulletin = BulletinHelper.show_success
|
|
510
|
+
run_on_ui_thread(lambda: bulletin(msg))
|
|
511
|
+
|
|
512
|
+
class Return:
|
|
513
|
+
@staticmethod
|
|
514
|
+
def _range(r: list) -> Any:
|
|
515
|
+
for i in r: yield i
|
|
516
|
+
@staticmethod
|
|
517
|
+
def _count(c: int) -> Any:
|
|
518
|
+
for i in range(c): yield i
|
|
519
|
+
@staticmethod
|
|
520
|
+
def _strings(sl: list[str]) -> Any:
|
|
521
|
+
for s in sl: yield s
|
|
522
|
+
|
|
523
|
+
class ListImport:
|
|
524
|
+
def _clear_modules_str(self, modules: str) -> str:
|
|
525
|
+
_modules = _clear_str(modules)
|
|
526
|
+
_modules = _modules.replace(" ", "")
|
|
527
|
+
_modules = _modules.replace("\n", "")
|
|
528
|
+
return _modules
|
|
529
|
+
def _import(self, modules: str) -> list:
|
|
530
|
+
_modules = self._clear_modules_str(modules)
|
|
531
|
+
_modules_list = []
|
|
532
|
+
if _modules:
|
|
533
|
+
index = _modules.find(",")
|
|
534
|
+
if index is not -1:
|
|
535
|
+
_names = _modules.split(",")
|
|
536
|
+
if _names:
|
|
537
|
+
for module in _names: _modules_list.append(module)
|
|
538
|
+
return _modules_list
|
|
539
|
+
|
|
540
|
+
def _clear_cache():
|
|
541
|
+
cache_dir = get_cache_dir()
|
|
542
|
+
if cache_dir:
|
|
543
|
+
shutil.rmtree(cache_dir, ignore_errors=True)
|
|
544
|
+
os.mkdir(cache_dir)
|
|
545
|
+
|
|
546
|
+
# Сразу говорю, это не получение директории, а получение самих плагинов
|
|
547
|
+
def _get_plugins() -> list:
|
|
548
|
+
plugins_dir = get_plugins_dir()
|
|
549
|
+
plugins = []
|
|
550
|
+
if plugins_dir: plugins = list_dir(path=plugins_dir, extensions=[".py", ".plugin"])
|
|
551
|
+
return plugins
|
|
552
|
+
|
|
553
|
+
@deprecated("AndroidUtilities.getPropertyString")
|
|
554
|
+
def _verify_getprop_string(s: str) -> bool:
|
|
555
|
+
deny_chars = [
|
|
556
|
+
"`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "_", "=", "+", "/", "\\",
|
|
557
|
+
"?", ",", "\"", "'", "|", "<", ">"
|
|
558
|
+
]
|
|
559
|
+
|
|
560
|
+
status = []
|
|
561
|
+
_last_status = True
|
|
562
|
+
|
|
563
|
+
for char in deny_chars:
|
|
564
|
+
status.append(not s.__contains__(char))
|
|
565
|
+
|
|
566
|
+
for _status in status:
|
|
567
|
+
if not _status:
|
|
568
|
+
_last_status = False
|
|
569
|
+
break
|
|
570
|
+
|
|
571
|
+
return _last_status
|
|
572
|
+
|
|
573
|
+
@deprecated("Используйте AndroidUtilities.getPropertyString")
|
|
574
|
+
class AndroidUtils:
|
|
575
|
+
@staticmethod
|
|
576
|
+
def getprop(s: str) -> dict:
|
|
577
|
+
if _verify_getprop_string(s):
|
|
578
|
+
output = Subprocess.Run(["getprop", s])
|
|
579
|
+
_output = {
|
|
580
|
+
"stdout": output.stdout,
|
|
581
|
+
"stderr": output.stderr
|
|
582
|
+
}
|
|
583
|
+
return _output
|
|
584
|
+
output = {
|
|
585
|
+
"stdout": None,
|
|
586
|
+
"stderr": None
|
|
587
|
+
}
|
|
588
|
+
return output
|
|
589
|
+
|
|
590
|
+
class LocaleManager:
|
|
591
|
+
def __init__(self, strings: dict): self.strings = strings
|
|
592
|
+
def _get_lang(self) -> str:
|
|
593
|
+
java = _get_java_classes()
|
|
594
|
+
Locale = java.get("util.Locale")
|
|
595
|
+
default = Locale.getDefault()
|
|
596
|
+
lang = default.getLanguage()
|
|
597
|
+
return lang
|
|
598
|
+
def _get_string(self, lang: str, _str_id: str) -> str:
|
|
599
|
+
string = self.strings[lang][_str_id]
|
|
600
|
+
return string
|
|
601
|
+
def get_string(self, _str_id: str) -> str:
|
|
602
|
+
lang = self._get_lang()
|
|
603
|
+
string = self._get_string(lang, _str_id)
|
|
604
|
+
return string
|
|
605
|
+
|
|
606
|
+
# Тута базовый интерпритатор Forth
|
|
607
|
+
class Forth:
|
|
608
|
+
def __init__(self):
|
|
609
|
+
self.stack = []
|
|
610
|
+
self.words = {
|
|
611
|
+
".": lambda: _log(self.stack.pop()),
|
|
612
|
+
"+": lambda: self.stack.append(self.stack.pop() + self.stack.pop()),
|
|
613
|
+
"-": lambda: self.stack.append(-self.stack.pop() + self.stack.pop()),
|
|
614
|
+
"*": lambda: self.stack.append(self.stack.pop() * self.stack.pop()),
|
|
615
|
+
"/": lambda: self.stack.append(1 // self.stack.pop() * self.stack.pop()),
|
|
616
|
+
"drop": lambda: self.stack.pop(),
|
|
617
|
+
"swap": lambda: (self.stack.append(self.stack.pop(-2)), self.stack.pop(-2)),
|
|
618
|
+
"dup": lambda: self.stack.append(self.stack[-1])
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
def _exec(self, code: str):
|
|
622
|
+
_code = code.lower()
|
|
623
|
+
|
|
624
|
+
for i in _code.split():
|
|
625
|
+
if i in self.words:
|
|
626
|
+
self.words[i]()
|
|
627
|
+
else:
|
|
628
|
+
try:
|
|
629
|
+
self.stack.append(int(i))
|
|
630
|
+
except Exception:
|
|
631
|
+
self.stack.append(i)
|
|
632
|
+
|
|
633
|
+
class Const:
|
|
634
|
+
def __setattr__(self, name: str, value: Any):
|
|
635
|
+
if name in self.__dict__:
|
|
636
|
+
raise Exception("const")
|
|
637
|
+
super().__setattr__(name, value)
|
|
638
|
+
|
|
639
|
+
class JSONError(Exception):
|
|
640
|
+
...
|
|
641
|
+
|
|
642
|
+
class TrueString(str):
|
|
643
|
+
def __bool__(self):
|
|
644
|
+
return True
|
|
645
|
+
|
|
646
|
+
class FalseString(str):
|
|
647
|
+
def __bool__(self):
|
|
648
|
+
return False
|
|
649
|
+
|
|
650
|
+
# :)
|
|
651
|
+
class String(TrueString):
|
|
652
|
+
pass
|
|
653
|
+
|
|
654
|
+
class MoonLib(BasePlugin):
|
|
655
|
+
vib_blink = None
|
|
656
|
+
path = None
|
|
657
|
+
data = """
|
|
658
|
+
DON'T TOUCH THIS FILE
|
|
659
|
+
|
|
660
|
+
This is file created for check plugin new install
|
|
661
|
+
|
|
662
|
+
By MLevankov
|
|
663
|
+
"""
|
|
664
|
+
|
|
665
|
+
def __init__(self):
|
|
666
|
+
super().__init__()
|
|
667
|
+
|
|
668
|
+
self.log = _log
|
|
669
|
+
self.versions = None
|
|
670
|
+
self.changes = None
|
|
671
|
+
def on_plugin_load(self):
|
|
672
|
+
self.log("[MoonLib] Plugin is successfully loaded")
|
|
673
|
+
self.add_on_send_message_hook()
|
|
674
|
+
self.log("[MoonLib] Message hook is successfully added")
|
|
675
|
+
ctx = _get_ctx()
|
|
676
|
+
self.path = f"{ctx.getFilesDir().getAbsolutePath()}/moonlib.txt"
|
|
677
|
+
new_install = self.is_new_install()
|
|
678
|
+
if new_install:
|
|
679
|
+
self.vib_blink = VibBlink()
|
|
680
|
+
self.vib_blink.blink()
|
|
681
|
+
self.log("[MoonLib] Vibrator is successfully called")
|
|
682
|
+
file = File()
|
|
683
|
+
file.Write(self.path, "UTF-8", self.data)
|
|
684
|
+
self.log("[MoonLib] File is successfully created")
|
|
685
|
+
|
|
686
|
+
def on_plugin_unload(self):
|
|
687
|
+
self.log("[MoonLib] Plugin is successfully unloaded")
|
|
688
|
+
|
|
689
|
+
def on_send_message_hook(self, account: int, params: Any) -> HookResult:
|
|
690
|
+
if params.message is ".moon":
|
|
691
|
+
self.vib_blink = VibBlink()
|
|
692
|
+
self.vib_blink.blink()
|
|
693
|
+
params.message = "By MLevankov :3"
|
|
694
|
+
return HookResult(strategy=HookStrategy.MODIFY, params=params)
|
|
695
|
+
|
|
696
|
+
def is_new_install(self) -> bool:
|
|
697
|
+
if File.Exists(self.path):
|
|
698
|
+
file_data = File.Read(self.path, "UTF-8")
|
|
699
|
+
if file_data is self.data:
|
|
700
|
+
return False
|
|
701
|
+
return False
|
|
702
|
+
return True
|
|
703
|
+
|
|
704
|
+
def create_settings(self) -> List[Any]:
|
|
705
|
+
# Ласт этап инициализации будет перед созданием настроек
|
|
706
|
+
self._end_initialization()
|
|
707
|
+
|
|
708
|
+
settings = [
|
|
709
|
+
Header(text="Настройки"),
|
|
710
|
+
Switch(key="enable_moon_command", text="Включить команду .moon", default=True)
|
|
711
|
+
]
|
|
712
|
+
return settings
|
|
713
|
+
|
|
714
|
+
def _end_initialization(self):
|
|
715
|
+
def download():
|
|
716
|
+
try:
|
|
717
|
+
response = requests.get(
|
|
718
|
+
"https://github.com/MLevankov/moonlib/raw/refs/heads/master/changelog.json",
|
|
719
|
+
timeout=60
|
|
720
|
+
)
|
|
721
|
+
response.raise_for_status()
|
|
722
|
+
path = f"{self._get_absolute_path()}/changelog.json"
|
|
723
|
+
|
|
724
|
+
with open(path, "wb") as file:
|
|
725
|
+
file.write(response.content)
|
|
726
|
+
|
|
727
|
+
with open(path, "r", encoding="UTF-8") as file:
|
|
728
|
+
changelog = json.load(file)
|
|
729
|
+
if isinstance(changelog, dict):
|
|
730
|
+
self.versions = changelog.get("versions")
|
|
731
|
+
self.changes = changelog.get("changes")
|
|
732
|
+
|
|
733
|
+
if (
|
|
734
|
+
self.versions and
|
|
735
|
+
self.changes
|
|
736
|
+
):
|
|
737
|
+
self._changelog()
|
|
738
|
+
else:
|
|
739
|
+
raise JSONError("version && changes == none")
|
|
740
|
+
except Exception as ex:
|
|
741
|
+
self.log(f"[MoonLib] {ex}")
|
|
742
|
+
|
|
743
|
+
thread = threading.Thread(target=download, daemon=True)
|
|
744
|
+
thread.start()
|
|
745
|
+
|
|
746
|
+
def _get_absolute_path(self) -> str:
|
|
747
|
+
ctx = _get_ctx()
|
|
748
|
+
return ctx.getFilesDir().getAbsolutePath()
|
|
749
|
+
|
|
750
|
+
def _changelog(self):
|
|
751
|
+
from base_plugin import(
|
|
752
|
+
MenuItemData,
|
|
753
|
+
MenuItemType
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
self.add_menu_item(
|
|
757
|
+
MenuItemData(
|
|
758
|
+
menu_type=MenuItemType.DRAWER_MENU,
|
|
759
|
+
text="MoonLib ChangeLog",
|
|
760
|
+
on_click=self._show_changelog
|
|
761
|
+
)
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
from typing import Dict
|
|
765
|
+
|
|
766
|
+
def _show_changelog(self, context: Dict[str, Any]):
|
|
767
|
+
activity = get_last_fragment().getParentActivity()
|
|
768
|
+
|
|
769
|
+
builder = AlertDialogBuilder(activity)
|
|
770
|
+
|
|
771
|
+
msg = f"""
|
|
772
|
+
Изменения в либе:
|
|
773
|
+
Версии: {self.versions}
|
|
774
|
+
{self.changes}
|
|
775
|
+
"""
|
|
776
|
+
|
|
777
|
+
def on_positive_click(bld: AlertDialogBuilder, which: int):
|
|
778
|
+
bld.dismiss()
|
|
779
|
+
|
|
780
|
+
def on_negative_click(bld: AlertDialogBuilder, which: int):
|
|
781
|
+
bld.dismiss()
|
|
782
|
+
|
|
783
|
+
builder.set_title("ChangeLog")
|
|
784
|
+
builder.set_message(msg)
|
|
785
|
+
builder.set_positive_button("OK", on_positive_click)
|
|
786
|
+
builder.set_negative_button("Cancel", on_negative_click)
|
|
787
|
+
builder.create()
|
|
788
|
+
builder.set_cancelable(True)
|
|
789
|
+
builder.show()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: moonlib
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Мини либа для работы с java классами, и т.д
|
|
5
|
+
Author: @MLPlugins
|
|
6
|
+
License: @MoonLicence
|
|
7
|
+
License-File: LICENCE
|
|
8
|
+
Dynamic: author
|
|
9
|
+
Dynamic: license
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
Dynamic: summary
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
main.py,sha256=5sOmlQ3liTqpOZW5umEuyJACMd4lv_EZrXTBTWyILhs,26856
|
|
2
|
+
moonlib-1.0.0.dist-info/licenses/LICENCE,sha256=2QA-eWMIwR12hsp2O2S_b1sSyVlNBDWavUgvGPKqO1g,13
|
|
3
|
+
moonlib-1.0.0.dist-info/METADATA,sha256=CVRja6xq6Y6vlNV-dMiMET5mAwpEI-YkB_PHgFcJT6Y,268
|
|
4
|
+
moonlib-1.0.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
5
|
+
moonlib-1.0.0.dist-info/top_level.txt,sha256=ZAMgPdWghn6xTRBO6Kc3ML1y3ZrZLnjZlqbboKXc_AE,5
|
|
6
|
+
moonlib-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@MoonLicence
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
main
|