telegrinder 0.2.0.post2__py3-none-any.whl → 0.2.1__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.

Potentially problematic release.


This version of telegrinder might be problematic. Click here for more details.

telegrinder/__init__.py CHANGED
@@ -79,6 +79,7 @@ from .bot import (
79
79
  WaiterMachine,
80
80
  register_manager,
81
81
  )
82
+ from .bot.rules import StateMeta
82
83
  from .client import ABCClient, AiohttpClient
83
84
  from .model import Model
84
85
  from .modules import logger
@@ -86,6 +87,7 @@ from .tools import (
86
87
  ABCErrorHandler,
87
88
  ABCGlobalContext,
88
89
  ABCLoopWrapper,
90
+ ABCStateStorage,
89
91
  ABCTranslator,
90
92
  ABCTranslatorMiddleware,
91
93
  AnyMarkup,
@@ -104,10 +106,12 @@ from .tools import (
104
106
  KeyboardSetYAML,
105
107
  Lifespan,
106
108
  LoopWrapper,
109
+ MemoryStateStorage,
107
110
  ParseMode,
108
111
  RowButtons,
109
112
  SimpleI18n,
110
113
  SimpleTranslator,
114
+ StateData,
111
115
  ctx_var,
112
116
  magic_bundle,
113
117
  )
@@ -207,4 +211,8 @@ __all__ = (
207
211
  "logger",
208
212
  "magic_bundle",
209
213
  "register_manager",
214
+ "ABCStateStorage",
215
+ "MemoryStateStorage",
216
+ "StateData",
217
+ "StateMeta",
210
218
  )
@@ -45,13 +45,13 @@ class WaiterMachine:
45
45
 
46
46
  async def drop(
47
47
  self,
48
- state_view: "ABCStateView[EventModel]",
48
+ state_view: "ABCStateView[EventModel] | str",
49
49
  id: Identificator,
50
50
  event: EventModel,
51
51
  update: Update,
52
52
  **context: typing.Any,
53
53
  ) -> None:
54
- view_name = state_view.__class__.__name__
54
+ view_name = state_view if isinstance(state_view, str) else state_view.__class__.__name__
55
55
  if view_name not in self.storage:
56
56
  raise LookupError("No record of view {!r} found.".format(view_name))
57
57
 
@@ -61,13 +61,26 @@ class WaiterMachine:
61
61
 
62
62
  short_state.cancel()
63
63
  await self.call_behaviour(
64
- state_view,
65
64
  event,
66
65
  update,
67
66
  behaviour=short_state.on_drop_behaviour,
68
67
  **context,
69
68
  )
70
69
 
70
+ async def drop_all(self) -> None:
71
+ """Drops all waiters in storage"""
72
+ for view_name in self.storage:
73
+ for ident, short_state in self.storage[view_name].items():
74
+ if short_state.context:
75
+ await self.drop(
76
+ view_name,
77
+ ident,
78
+ short_state.context.event,
79
+ short_state.context.context.raw_update
80
+ )
81
+ else:
82
+ short_state.cancel()
83
+
71
84
  async def wait(
72
85
  self,
73
86
  state_view: "BaseStateView[EventModel]",
@@ -114,7 +127,6 @@ class WaiterMachine:
114
127
 
115
128
  async def call_behaviour(
116
129
  self,
117
- view: "ABCStateView[EventModel]",
118
130
  event: EventModel,
119
131
  update: Update,
120
132
  behaviour: Behaviour[EventModel] | None = None,
@@ -147,7 +159,7 @@ class WaiterMachine:
147
159
  now = datetime.datetime.now()
148
160
  for ident, short_state in self.storage.get(view_name, {}).copy().items():
149
161
  if short_state.expiration_date is not None and now > short_state.expiration_date:
150
- assert short_state.context
162
+ assert short_state.context # FIXME: why???
151
163
  await self.drop(
152
164
  view,
153
165
  ident,
@@ -59,7 +59,6 @@ class WaiterMiddleware(ABCMiddleware[EventType]):
59
59
 
60
60
  # before running the handler we check if the user wants to exit waiting
61
61
  if short_state.exit_behaviour is not None and await self.machine.call_behaviour(
62
- self.view,
63
62
  event,
64
63
  ctx.raw_update,
65
64
  behaviour=short_state.exit_behaviour,
@@ -87,7 +86,6 @@ class WaiterMiddleware(ABCMiddleware[EventType]):
87
86
 
88
87
  elif short_state.default_behaviour is not None:
89
88
  await self.machine.call_behaviour(
90
- self.view,
91
89
  event,
92
90
  ctx.raw_update,
93
91
  behaviour=short_state.default_behaviour,
@@ -53,6 +53,7 @@ from telegrinder.bot.rules.node import NodeRule
53
53
  from telegrinder.bot.rules.regex import Regex
54
54
  from telegrinder.bot.rules.rule_enum import RuleEnum
55
55
  from telegrinder.bot.rules.start import StartCommand
56
+ from telegrinder.bot.rules.state import State, StateMeta
56
57
  from telegrinder.bot.rules.text import HasText, Text
57
58
  from telegrinder.bot.rules.update import IsUpdateType
58
59
 
@@ -113,4 +114,6 @@ __all__ = (
113
114
  "StartCommand",
114
115
  "Text",
115
116
  "NodeRule",
117
+ "State",
118
+ "StateMeta",
116
119
  )
@@ -0,0 +1,34 @@
1
+ import enum
2
+ import typing
3
+
4
+ from telegrinder.bot.dispatch.context import Context
5
+ from telegrinder.bot.rules.abc import ABCRule
6
+ from telegrinder.node import Source
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from telegrinder.tools.state_storage import ABCStateStorage
10
+
11
+
12
+ class StateMeta(enum.Enum):
13
+ NO_STATE = enum.auto()
14
+ ANY = enum.auto()
15
+
16
+
17
+ class State(ABCRule):
18
+ def __init__(self, storage: "ABCStateStorage", key: str | StateMeta = StateMeta.ANY):
19
+ self.storage = storage
20
+ self.key = key
21
+
22
+ async def check(self, source: Source, ctx: Context) -> bool:
23
+ state = await self.storage.get(source.from_user.id)
24
+ if not state:
25
+ return self.key == StateMeta.NO_STATE
26
+
27
+ if self.key != StateMeta.ANY and self.key != state.unwrap().key:
28
+ return False
29
+
30
+ ctx["state"] = state.unwrap()
31
+ return True
32
+
33
+
34
+ __all__ = ("State", "StateMeta")
@@ -67,6 +67,7 @@ from .limited_dict import LimitedDict
67
67
  from .loop_wrapper import ABCLoopWrapper, DelayedTask, Lifespan, LoopWrapper
68
68
  from .magic import impl, magic_bundle, resolve_arg_names
69
69
  from .parse_mode import ParseMode
70
+ from .state_storage import ABCStateStorage, MemoryStateStorage, StateData
70
71
 
71
72
  __all__ = (
72
73
  "ABCErrorHandler",
@@ -139,4 +140,7 @@ __all__ = (
139
140
  "block_quote",
140
141
  "impl",
141
142
  "resolve_arg_names",
143
+ "ABCStateStorage",
144
+ "MemoryStateStorage",
145
+ "StateData",
142
146
  )
@@ -26,11 +26,6 @@ class KeyboardModel:
26
26
  keyboard: list[list[DictStrAny]]
27
27
 
28
28
 
29
-
30
- def copy_keyboard(keyboard: list[list[DictStrAny]]) -> list[list[DictStrAny]]:
31
- return [row.copy() for row in keyboard]
32
-
33
-
34
29
  class ABCMarkup(ABC, typing.Generic[ButtonT]):
35
30
  BUTTON: type[ButtonT]
36
31
  keyboard: list[list[DictStrAny]]
@@ -78,7 +73,7 @@ class ABCMarkup(ABC, typing.Generic[ButtonT]):
78
73
  return copy_keyboard
79
74
 
80
75
  def merge(self, other: typing.Self) -> typing.Self:
81
- self.keyboard.extend(copy_keyboard(other.keyboard))
76
+ self.keyboard.extend(other.keyboard)
82
77
  return self
83
78
 
84
79
 
@@ -0,0 +1,4 @@
1
+ from .abc import ABCStateStorage, StateData
2
+ from .memory import MemoryStateStorage
3
+
4
+ __all__ = ("ABCStateStorage", "StateData", "MemoryStateStorage")
@@ -0,0 +1,33 @@
1
+ import abc
2
+ import typing
3
+ from dataclasses import dataclass
4
+
5
+ from fntypes import Option
6
+
7
+ from telegrinder.bot.rules.state import State, StateMeta
8
+
9
+ Payload = typing.TypeVar("Payload")
10
+
11
+
12
+ @dataclass
13
+ class StateData(typing.Generic[Payload]):
14
+ key: str
15
+ payload: Payload
16
+
17
+
18
+ class ABCStateStorage(abc.ABC, typing.Generic[Payload]):
19
+ @abc.abstractmethod
20
+ async def get(self, user_id: int) -> Option[StateData[Payload]]:
21
+ ...
22
+
23
+ @abc.abstractmethod
24
+ async def delete(self, user_id: int) -> None:
25
+ ...
26
+
27
+ @abc.abstractmethod
28
+ async def set(self, user_id: int, key: str, payload: Payload) -> None:
29
+ ...
30
+
31
+ def State(self, key: str | StateMeta = StateMeta.ANY) -> State: # noqa: N802
32
+ """Can be used as a shortcut to get a state rule dependant on current storage"""
33
+ return State(storage=self, key=key)
@@ -0,0 +1,22 @@
1
+ import typing
2
+
3
+ from fntypes import Nothing, Option, Some
4
+
5
+ from .abc import ABCStateStorage, StateData
6
+
7
+ Payload = dict[str, typing.Any]
8
+
9
+
10
+ class MemoryStateStorage(ABCStateStorage[Payload]):
11
+ def __init__(self):
12
+ self.storage: dict[int, StateData[Payload]] = {}
13
+
14
+ async def get(self, user_id: int) -> Option[StateData[Payload]]:
15
+ state = self.storage.get(user_id)
16
+ return Some(state) if state is not None else Nothing()
17
+
18
+ async def set(self, user_id: int, key: str, payload: Payload) -> None:
19
+ self.storage[user_id] = StateData(key, payload)
20
+
21
+ async def delete(self, user_id: int) -> None:
22
+ self.storage.pop(user_id)
@@ -4178,7 +4178,7 @@ class APIMethods:
4178
4178
  :param description: Product description, 1-255 characters.
4179
4179
 
4180
4180
  :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to \
4181
- the user, use for your internal processes.
4181
+ the user, use it for your internal processes.
4182
4182
 
4183
4183
  :param provider_token: Payment provider token, obtained via @BotFather. Pass an empty string \
4184
4184
  for payments in Telegram Stars.
@@ -4296,7 +4296,7 @@ class APIMethods:
4296
4296
  :param description: Product description, 1-255 characters.
4297
4297
 
4298
4298
  :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to \
4299
- the user, use for your internal processes.
4299
+ the user, use it for your internal processes.
4300
4300
 
4301
4301
  :param provider_token: Payment provider token, obtained via @BotFather. Pass an empty string \
4302
4302
  for payments in Telegram Stars.
@@ -2795,6 +2795,15 @@ class ChatInviteLink(Model):
2795
2795
  pending_join_request_count: Option[int] = Nothing
2796
2796
  """Optional. Number of pending join requests created using this link."""
2797
2797
 
2798
+ subscription_period: Option[int] = Nothing
2799
+ """Optional. The number of seconds the subscription will be active for before
2800
+ the next payment."""
2801
+
2802
+ subscription_price: Option[int] = Nothing
2803
+ """Optional. The amount of Telegram Stars a user must pay initially and after
2804
+ each subsequent subscription period to be a member of the chat using the
2805
+ link."""
2806
+
2798
2807
 
2799
2808
  class ChatAdministratorRights(Model):
2800
2809
  """Object `ChatAdministratorRights`, see the [documentation](https://core.telegram.org/bots/api#chatadministratorrights).
@@ -5393,7 +5402,7 @@ class InputInvoiceMessageContent(InputMessageContent):
5393
5402
 
5394
5403
  payload: str
5395
5404
  """Bot-defined invoice payload, 1-128 bytes. This will not be displayed to
5396
- the user, use for your internal processes."""
5405
+ the user, use it for your internal processes."""
5397
5406
 
5398
5407
  currency: Currency
5399
5408
  """Three-letter ISO 4217 currency code, see more on currencies. Pass `XTR`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: telegrinder
3
- Version: 0.2.0.post2
3
+ Version: 0.2.1
4
4
  Summary: Modern visionary telegram bot framework.
5
5
  Home-page: https://github.com/timoniq/telegrinder
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- telegrinder/__init__.py,sha256=6w6pzPVw-MOX4zBcNLxEeWfX5yCAbrltcjILhs0yA1I,4219
1
+ telegrinder/__init__.py,sha256=vngxup29t8Hv4mnFFI9MZHKO33i0i3ny8gDa8KzgqQ8,4395
2
2
  telegrinder/api/__init__.py,sha256=AFZ07UvdL4rhq9lZpB-j9JuGOONmfkvt8RfdG71ND38,226
3
3
  telegrinder/api/api.py,sha256=OSwm2_mgHS9jCsKy_vdOJzVtH-j6v68ypkdtvKFlQ3w,2877
4
4
  telegrinder/api/error.py,sha256=6_KBR819Tg9mLI7w5aHHYnrS8VSDYNkWIrHCnfgCFKk,422
@@ -41,13 +41,13 @@ telegrinder/bot/dispatch/view/inline_query.py,sha256=wgel-euw601uk5qHIUZF7vyuhP5
41
41
  telegrinder/bot/dispatch/view/message.py,sha256=FzfL8Yn42exnvMj8VuKwNlKaHPhij5eBzLCrju7FEc0,1356
42
42
  telegrinder/bot/dispatch/view/raw.py,sha256=XMlJ0-XeuUfFUjSaI7ApR-gcGmSZ1eB_USHsCKdeMyY,3536
43
43
  telegrinder/bot/dispatch/waiter_machine/__init__.py,sha256=IeoNZIxAut4QZCqiQrA6g_BcSFyUdA-oFgfBhsG6ipA,363
44
- telegrinder/bot/dispatch/waiter_machine/machine.py,sha256=bMMYSwQPZbu58GYk364xu10_G8pLv0OAbvQRz69RhOs,5954
45
- telegrinder/bot/dispatch/waiter_machine/middleware.py,sha256=f4IJgjMwACOetmAIo6mey5WYOmO3Sw3OG3pga5ptsCc,3528
44
+ telegrinder/bot/dispatch/waiter_machine/machine.py,sha256=DhRtWrVasYsQtB-VkcxVGNbU46HftDLxMZxh1cBH9qU,6492
45
+ telegrinder/bot/dispatch/waiter_machine/middleware.py,sha256=1qQYT77foCSjtcxbMkiay4rPCGNPJJWaQOPEF353830,3478
46
46
  telegrinder/bot/dispatch/waiter_machine/short_state.py,sha256=V1NZUwM_pjO-NVvN1kOnx4yo6Mdt5SXMmQnSY5xp21w,2116
47
47
  telegrinder/bot/polling/__init__.py,sha256=OqfIFPS_V6UrCg-vCv9pkMFzTKdNbDP2faBfATs_TGg,94
48
48
  telegrinder/bot/polling/abc.py,sha256=qFiKzWTWENK-sSuShC5cPlM-JS4In2c8-1_ARdwdTms,442
49
49
  telegrinder/bot/polling/polling.py,sha256=w6_d6hOWY2ER5UG6EgGl2ceLvgJNDjeK13sUUmxWcfc,4714
50
- telegrinder/bot/rules/__init__.py,sha256=0uKfqa0myqjIBcwzfq5YxoRn0aZFR4IlQ3joFROIFEA,2772
50
+ telegrinder/bot/rules/__init__.py,sha256=ue5Lt9BoaUcwIJcwBItQR80_DdNZ2MznNmki2MIJyNs,2859
51
51
  telegrinder/bot/rules/abc.py,sha256=-y5dlftZZaYVPuvJsEmgYcM9nd8MDj3mdlN4Et1Vje8,6122
52
52
  telegrinder/bot/rules/adapter/__init__.py,sha256=kWcGnK0N26WsOwtF2n_yy1M2YMOtOp75931-ssVtOps,445
53
53
  telegrinder/bot/rules/adapter/abc.py,sha256=lPyieSDDr1z98rZ4W6Ic8QzHDzCEU9xIYrPKU9qDCas,682
@@ -72,6 +72,7 @@ telegrinder/bot/rules/node.py,sha256=IP7Ke-4hILb12XHNIresMf_87kXhlLfKOloShoQvWN0
72
72
  telegrinder/bot/rules/regex.py,sha256=mjXXPX-NB2e3gFU6LFihLnKANcgUoAL5lOnfmhC3Qnc,1153
73
73
  telegrinder/bot/rules/rule_enum.py,sha256=35GwPKLBTG_ESn4TZLcFJTLNjYXAi_d9wrfIDexoEH8,2113
74
74
  telegrinder/bot/rules/start.py,sha256=3ciA28m9JNcM-1H2YnLrUNyS1Y4UsyYPR5MClrpdAdA,1154
75
+ telegrinder/bot/rules/state.py,sha256=WxVsNRGVCJkzyLt48wtYXFkbXfFB2a7WpToGK3Pc0Uk,890
75
76
  telegrinder/bot/rules/text.py,sha256=h8SBiqF3_kkITDRCeDDanmNG80NRoSgj_dutjoV-26Y,964
76
77
  telegrinder/bot/rules/update.py,sha256=k73pZzlzhw77gHAmeIsqehy0-EI2ec8wVGxGRIF_8ao,398
77
78
  telegrinder/bot/scenario/__init__.py,sha256=nnPjdxdvjoEYYMRUEfWvIhZStiY1C984x1azdRRP9II,136
@@ -105,7 +106,7 @@ telegrinder/node/tools/generator.py,sha256=xuhZ5-TwoctFLlDBdYvjOQxHTpqC4IAdnkzMV
105
106
  telegrinder/node/update.py,sha256=2baHe10IJ0f6oOeQIqBXduRio9PGVd5Q2lbGyfU9yU8,441
106
107
  telegrinder/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
108
  telegrinder/rules.py,sha256=qQHT63Yek8I2zZFbfH2SzvQLUdZCcWBz-DFlz05i39I,1115
108
- telegrinder/tools/__init__.py,sha256=N_EGjedn096mPw1Tozc4uMCSXH2h9NCFnKuubFZu5ec,2837
109
+ telegrinder/tools/__init__.py,sha256=xFx3DfmBoHPp2xmjaxpYmSDjR1CFlmWIHLxd9-9hpCY,2977
109
110
  telegrinder/tools/buttons.py,sha256=4Mc8KZ8i8l2XSiJaybbO23ywIx8T3bQiMH0UkyXuj4I,2905
110
111
  telegrinder/tools/error_handler/__init__.py,sha256=WmYWZCNhhSk32j4lIOltEwzoYUx086TGTbOF5h3Ps7s,207
111
112
  telegrinder/tools/error_handler/abc.py,sha256=BpBtysvMZKxAYAIrQHYXNxP9mCZRzjtshFFwzIXvVi8,882
@@ -127,19 +128,22 @@ telegrinder/tools/i18n/simple.py,sha256=yiaGtEAaMkrzYV3O7esA2wi3A2n9YyndxKPKp8Fs
127
128
  telegrinder/tools/kb_set/__init__.py,sha256=k1KCQTnvEgJ2y4KlghhJWOh5rccwg_27cs8264NtMmk,156
128
129
  telegrinder/tools/kb_set/base.py,sha256=mbZs-ViUErfSibzyN064IqZp76LBJPg3NB4D9v4VFtg,243
129
130
  telegrinder/tools/kb_set/yaml.py,sha256=glk27r0L9Y8ibaPmTHMAEJZw-ED-ehmSo_NdJNKkrNs,2007
130
- telegrinder/tools/keyboard.py,sha256=bEC7NqLcuYIxH6CPBH8oykXD5Ud-CuTpl035FlK5SEc,3825
131
+ telegrinder/tools/keyboard.py,sha256=3zaBOMWMV5rPQg6BsLI7S_6_Nhyl2yKosFCbQAk9GzM,3684
131
132
  telegrinder/tools/limited_dict.py,sha256=tb1WT4P3Oia5CsCBXTHzlFjrPnIgf9eHCkQ0zhAbEUg,1078
132
133
  telegrinder/tools/loop_wrapper/__init__.py,sha256=ZQ5jmE1lOKnqJlMZ9k2OYmjvOEhOlHPijUWqZ4nHIgk,165
133
134
  telegrinder/tools/loop_wrapper/abc.py,sha256=ET_Dp-kRz75Jo1fZB3qofUgEXN4FqlU0xH2diESKGCM,266
134
135
  telegrinder/tools/loop_wrapper/loop_wrapper.py,sha256=k5NaZ-18c8zuzBHpHNk03_33dHFNww8HiCJEGz2g5W0,6815
135
136
  telegrinder/tools/magic.py,sha256=oPaEqqtbFEVXHVCjdbmUvbjtWiQCw2yuMOfoQLLyFjE,4332
136
137
  telegrinder/tools/parse_mode.py,sha256=JyQ-x9YAMPLhIIiUX01acyKkpWgs5TBA07W-iUyPHpE,92
138
+ telegrinder/tools/state_storage/__init__.py,sha256=wQHybNOqF1O7ri_UUQSvtKgLuesQqa2wxETaEgMyMSU,149
139
+ telegrinder/tools/state_storage/abc.py,sha256=NAoEepnNGVv-SMoRrV-upKLQEIpJWtHIxZFbKWjnDAs,850
140
+ telegrinder/tools/state_storage/memory.py,sha256=KNEyAqjcCeZhk2YbLnoQbio9LU9s9VZzRnT7A5eEMMU,662
137
141
  telegrinder/types/__init__.py,sha256=_7ANa63K4rMRWHgSBSQJXi2mNMtOJgp_E4yGattsgJE,6612
138
142
  telegrinder/types/enums.py,sha256=q9URlXZvrjOUQKpLfV6v9uspBcLrdW0gtU-m8YDnj7w,19017
139
- telegrinder/types/methods.py,sha256=Ah7DVfyb99EJzYHCX9feW9hIGBYcnHLKEzu5MUp4rvg,201058
140
- telegrinder/types/objects.py,sha256=6bN1BsYZ-Kmh1GcXgtumfZvvLJqxjjjJ9OzyfqBtGZM,244504
143
+ telegrinder/types/methods.py,sha256=cZQzITIQbEVLLZCkscl8jMqDoS5tIqXKkIFyWJoi2-Q,201064
144
+ telegrinder/types/objects.py,sha256=0UQ60e8CoqqpRjLSG-9RqMaSro3qUF_LxMJXXhpW8Ng,244881
141
145
  telegrinder/verification_utils.py,sha256=X7N0mHoOzbcYeKa5XxI_EFhmEGX5XNU3qqgbV8YRRa4,987
142
- telegrinder-0.2.0.post2.dist-info/LICENSE,sha256=Q0tKgU8mPOCQAkc6m__BrNIpRge8mPBQJDd59s21NZo,1095
143
- telegrinder-0.2.0.post2.dist-info/METADATA,sha256=bKp_kZ28oIvDegWbLKZQw1o8imRU0LyO85H19q1XAOg,3008
144
- telegrinder-0.2.0.post2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
145
- telegrinder-0.2.0.post2.dist-info/RECORD,,
146
+ telegrinder-0.2.1.dist-info/LICENSE,sha256=Q0tKgU8mPOCQAkc6m__BrNIpRge8mPBQJDd59s21NZo,1095
147
+ telegrinder-0.2.1.dist-info/METADATA,sha256=o0lMt6wBzckvY4GaO7azBXsIjTXEuSwoDP1eaT2LRXQ,3002
148
+ telegrinder-0.2.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
149
+ telegrinder-0.2.1.dist-info/RECORD,,