summaryrefslogtreecommitdiff
path: root/python-pyromod.spec
blob: 5d2ed52dc2d841ff81a6a4919f7f0fff549cf291 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
%global _empty_manifest_terminate_build 0
Name:		python-pyromod
Version:	2.0.0
Release:	1
Summary:	A monkeypatcher add-on for Pyrogram that does conversation handling
License:	LGPLv3+
URL:		https://pypi.org/project/pyromod/
Source0:	https://mirrors.nju.edu.cn/pypi/web/packages/48/e2/9123bb8d324bcce50a9006de4ab7712693a0bfafa98e2443214b377e9871/pyromod-2.0.0.tar.gz
BuildArch:	noarch

Requires:	python3-pyrogram

%description
# pyromod
[![Telegram](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/pyromodchat)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/usernein/pyromod)
![GitHub commits since latest release (by date) for a branch](https://img.shields.io/github/commits-since/usernein/pyromod/latest)
![PyPI - Downloads](https://img.shields.io/pypi/dm/pyromod?color=388E3C)

A monkeypatcher add-on for Pyrogram which does conversation handling and other cool stuff.

In other words, it is a compilation of utilities i developed for improving my personal experience with Pyrogram.
It works **together** with Pyrogram, it is **not** a fork/modded version. It does monkeypatching to add features to Pyrogram classes on the go (so i don't need to update on every Pyrogram's release).

## Usage
Import `pyromod` one time in your script and you'll already be able to use the modified pyrogram in all your handlers. Example:
```python
# config.py
import pyromod
from pyrogram import Client

app = Client('my_session')
```

Then you can, from another file, do `from config import app` to import the modded Pyrogram Client we created above. It will be modded globally.

All the patches are applied automatically as soon as pyromod is imported.

## Methods
All pyromod methods are callable by any of these ways:
- `await Client.<method>(identifier, ...)`
- `await Chat.<method>()`
- `await User.<method>()`

In the last two, Pyrogram automatically gets the ids from the object, to compound the `identifier` tuple that `Client.listen` uses.

These are the methods pyromod adds:
- `listen(identifier, filters=None, listener_type=ListenerTypes.MESSAGE, timeout=None, unallowed_click_alert=True)`
Awaits for a new message in the specified chat and returns its Message object. If listener_type is set to `ListenerTypes.CALLBACK_QUERY`, it awaits and returns a CallbackQuery object.
You can pass Update Filters to the `filters` parameter just like you do for the update handlers. e.g. `filters=filters.photo & filters.bot`
`identifier` is a tuple containing, in this exact order, (chat_id, user_id, message_id). It lets you specify exactly which update you want. You don't need to worry about that if you mostly use the bound methods.
`unnalowed_click_alert` is the text that users will see in an alert when the button is not waiting for them to click. If True, it uses the default text at `PyromodConfig.unnalowed_click_alert_text`. If False, no text is shown.

- `ask(text, identifier, filters=None, listener_type=ListenerTypes.MESSAGE, timeout=None, unallowed_click_alert=True)`
Same as `listen`, but sends a message to identifier[0] before and only then waits for a response.
You can additionally pass any of the `Client.send_message()` parameters. Check the example below.
The object of the sent message is returned inside of the attribute `request`

Example:
```python
answer = await message.chat.ask('*Send me your name:*', parse_mode=enums.ParseMode.MARKDOWN)
await answer.request.edit_text("Name received!")
await answer.reply(f'Your name is: {answer.text}', quote=True)    
```

- `Message.wait_for_click(from_user_id=None, timeout=None, filters=None, alert=True)`
Awaits from a click on any button on the Message object. If `from_user_id` is passed, pyromod will wait for a click of that user.
If you pass any text to `alert`, it will be shown to any other user. If `alert` is True, it will use the default text. If False, no text will be shown.

## `pyromod.helpers`
Tools for creating inline keyboards a lot easier.

### `pyromod.helpers.ikb`

Creates a inline keyboard.
Its first and only argument is a list (the keyboard itself) containing lists (the lines) of buttons, which can be lists or tuples. I use tuples to avoid a mess with a lot of brackets. Tuples makes it easier to read.

The button syntax is very simple: `(TEXT, VALUE, TYPE)`, with TYPE being any existent button type (e.g. `url`) and VALUE is its value. If you omit the type, it will be considered as a callback button.
If you pass only a string as button, it will be used as text and callback_data for the InlineKeyboardButton.
This syntax will be automagically converted by pyromod.

Examples:
```python
from pyromod.helpers import ikb
...
keyboard = ikb([
    [('Button 1', 'call_1'), ('Button 2', 'call_2')],
    [('Another button', 't.me/pyromodchat', 'url')]
])
await message.reply('Easy inline keyboard', reply_markup=keyboard)
```

```python
keyboard = ikb([
	["Mars", "Earth", "Venus"],
	["Saturn", "Jupyter"]
])
await message.reply("Easiest inline keyboard", reply_markup=keyboard)
```

- `pyromod.helpers.array_chunk`
Chunk the elements of a list into small lists. i.e. [1, 2, 3, 4] can become [[1,2], [3,4]]. This is extremely useful if you want to build a keyboard dinamically with more than 1 column. You just put all buttons together in a list and run:
```python
lines = array_chunk(buttons, 2)
keyboard = ikb(lines)
```
This will generate a list of lines with 2 buttons on each one.

### `pyromod.nav`
Tools for creating navigation keyboards.

- `pyromod.nav.Pagination`
Creates a full paginated keyboard. Usage:
```python
from pyrogram import Client, filters
from pyromod.nav import Pagination
from pyromod.helpers import ikb

def page_data(page):
    return f'view_page {page}'
def item_data(item, page):
    return f'view_item {item} {page}'
def item_title(item, page):
    return f'Item {item} of page {page}'

@Client.on_message(filters.regex('/nav'))
async def on_nav(c,m):
    objects = [*range(1,100)]
    page = Pagination(
        objects,
        page_data=page_data, # callback to define the callback_data for page buttons in the bottom
        item_data=item_data, # callback to define the callback_data for each item button
        item_title=item_title # callback to define the text for each item button
    )
    index = 0 # in which page is it now? (used to calculate the offset)
    lines = 5 # how many lines of the keyboard to include for the items
    columns = how many columns include in each items' line
    kb = page.create(index, lines, columns)
    await m.reply('Test', reply_markup=ikb(kb))
```

## pyromod.PyrogramConfig
It lets you do some tweaks on pyromod behavior.
```python
class PyromodConfig:
    timeout_handler = None
    stopped_handler = None
    throw_exceptions = True
    unallowed_click_alert = True
    unallowed_click_alert_text = (
        "[pyromod] You're not expected to click this button."
    )
```
`timeout_handler` and `stopped_handler` are callbacks that receive (identifier, listener_data) as arguments. timeout_handler receives an extra arg `timeout`. When they are in use, pyromod won't throw the exceptions ListenerStopped and ListenedTimeout.

### Copyright & License
This project may include snippets of Pyrogram code
- Pyrogram - Telegram MTProto API Client Library for Python. Copyright (C) 2017-2022 Dan <<https://github.com/delivrance>>

Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)




%package -n python3-pyromod
Summary:	A monkeypatcher add-on for Pyrogram that does conversation handling
Provides:	python-pyromod
BuildRequires:	python3-devel
BuildRequires:	python3-setuptools
BuildRequires:	python3-pip
%description -n python3-pyromod
# pyromod
[![Telegram](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/pyromodchat)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/usernein/pyromod)
![GitHub commits since latest release (by date) for a branch](https://img.shields.io/github/commits-since/usernein/pyromod/latest)
![PyPI - Downloads](https://img.shields.io/pypi/dm/pyromod?color=388E3C)

A monkeypatcher add-on for Pyrogram which does conversation handling and other cool stuff.

In other words, it is a compilation of utilities i developed for improving my personal experience with Pyrogram.
It works **together** with Pyrogram, it is **not** a fork/modded version. It does monkeypatching to add features to Pyrogram classes on the go (so i don't need to update on every Pyrogram's release).

## Usage
Import `pyromod` one time in your script and you'll already be able to use the modified pyrogram in all your handlers. Example:
```python
# config.py
import pyromod
from pyrogram import Client

app = Client('my_session')
```

Then you can, from another file, do `from config import app` to import the modded Pyrogram Client we created above. It will be modded globally.

All the patches are applied automatically as soon as pyromod is imported.

## Methods
All pyromod methods are callable by any of these ways:
- `await Client.<method>(identifier, ...)`
- `await Chat.<method>()`
- `await User.<method>()`

In the last two, Pyrogram automatically gets the ids from the object, to compound the `identifier` tuple that `Client.listen` uses.

These are the methods pyromod adds:
- `listen(identifier, filters=None, listener_type=ListenerTypes.MESSAGE, timeout=None, unallowed_click_alert=True)`
Awaits for a new message in the specified chat and returns its Message object. If listener_type is set to `ListenerTypes.CALLBACK_QUERY`, it awaits and returns a CallbackQuery object.
You can pass Update Filters to the `filters` parameter just like you do for the update handlers. e.g. `filters=filters.photo & filters.bot`
`identifier` is a tuple containing, in this exact order, (chat_id, user_id, message_id). It lets you specify exactly which update you want. You don't need to worry about that if you mostly use the bound methods.
`unnalowed_click_alert` is the text that users will see in an alert when the button is not waiting for them to click. If True, it uses the default text at `PyromodConfig.unnalowed_click_alert_text`. If False, no text is shown.

- `ask(text, identifier, filters=None, listener_type=ListenerTypes.MESSAGE, timeout=None, unallowed_click_alert=True)`
Same as `listen`, but sends a message to identifier[0] before and only then waits for a response.
You can additionally pass any of the `Client.send_message()` parameters. Check the example below.
The object of the sent message is returned inside of the attribute `request`

Example:
```python
answer = await message.chat.ask('*Send me your name:*', parse_mode=enums.ParseMode.MARKDOWN)
await answer.request.edit_text("Name received!")
await answer.reply(f'Your name is: {answer.text}', quote=True)    
```

- `Message.wait_for_click(from_user_id=None, timeout=None, filters=None, alert=True)`
Awaits from a click on any button on the Message object. If `from_user_id` is passed, pyromod will wait for a click of that user.
If you pass any text to `alert`, it will be shown to any other user. If `alert` is True, it will use the default text. If False, no text will be shown.

## `pyromod.helpers`
Tools for creating inline keyboards a lot easier.

### `pyromod.helpers.ikb`

Creates a inline keyboard.
Its first and only argument is a list (the keyboard itself) containing lists (the lines) of buttons, which can be lists or tuples. I use tuples to avoid a mess with a lot of brackets. Tuples makes it easier to read.

The button syntax is very simple: `(TEXT, VALUE, TYPE)`, with TYPE being any existent button type (e.g. `url`) and VALUE is its value. If you omit the type, it will be considered as a callback button.
If you pass only a string as button, it will be used as text and callback_data for the InlineKeyboardButton.
This syntax will be automagically converted by pyromod.

Examples:
```python
from pyromod.helpers import ikb
...
keyboard = ikb([
    [('Button 1', 'call_1'), ('Button 2', 'call_2')],
    [('Another button', 't.me/pyromodchat', 'url')]
])
await message.reply('Easy inline keyboard', reply_markup=keyboard)
```

```python
keyboard = ikb([
	["Mars", "Earth", "Venus"],
	["Saturn", "Jupyter"]
])
await message.reply("Easiest inline keyboard", reply_markup=keyboard)
```

- `pyromod.helpers.array_chunk`
Chunk the elements of a list into small lists. i.e. [1, 2, 3, 4] can become [[1,2], [3,4]]. This is extremely useful if you want to build a keyboard dinamically with more than 1 column. You just put all buttons together in a list and run:
```python
lines = array_chunk(buttons, 2)
keyboard = ikb(lines)
```
This will generate a list of lines with 2 buttons on each one.

### `pyromod.nav`
Tools for creating navigation keyboards.

- `pyromod.nav.Pagination`
Creates a full paginated keyboard. Usage:
```python
from pyrogram import Client, filters
from pyromod.nav import Pagination
from pyromod.helpers import ikb

def page_data(page):
    return f'view_page {page}'
def item_data(item, page):
    return f'view_item {item} {page}'
def item_title(item, page):
    return f'Item {item} of page {page}'

@Client.on_message(filters.regex('/nav'))
async def on_nav(c,m):
    objects = [*range(1,100)]
    page = Pagination(
        objects,
        page_data=page_data, # callback to define the callback_data for page buttons in the bottom
        item_data=item_data, # callback to define the callback_data for each item button
        item_title=item_title # callback to define the text for each item button
    )
    index = 0 # in which page is it now? (used to calculate the offset)
    lines = 5 # how many lines of the keyboard to include for the items
    columns = how many columns include in each items' line
    kb = page.create(index, lines, columns)
    await m.reply('Test', reply_markup=ikb(kb))
```

## pyromod.PyrogramConfig
It lets you do some tweaks on pyromod behavior.
```python
class PyromodConfig:
    timeout_handler = None
    stopped_handler = None
    throw_exceptions = True
    unallowed_click_alert = True
    unallowed_click_alert_text = (
        "[pyromod] You're not expected to click this button."
    )
```
`timeout_handler` and `stopped_handler` are callbacks that receive (identifier, listener_data) as arguments. timeout_handler receives an extra arg `timeout`. When they are in use, pyromod won't throw the exceptions ListenerStopped and ListenedTimeout.

### Copyright & License
This project may include snippets of Pyrogram code
- Pyrogram - Telegram MTProto API Client Library for Python. Copyright (C) 2017-2022 Dan <<https://github.com/delivrance>>

Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)




%package help
Summary:	Development documents and examples for pyromod
Provides:	python3-pyromod-doc
%description help
# pyromod
[![Telegram](https://img.shields.io/badge/Telegram-2CA5E0?style=flat&logo=telegram&logoColor=white)](https://t.me/pyromodchat)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/usernein/pyromod)
![GitHub commits since latest release (by date) for a branch](https://img.shields.io/github/commits-since/usernein/pyromod/latest)
![PyPI - Downloads](https://img.shields.io/pypi/dm/pyromod?color=388E3C)

A monkeypatcher add-on for Pyrogram which does conversation handling and other cool stuff.

In other words, it is a compilation of utilities i developed for improving my personal experience with Pyrogram.
It works **together** with Pyrogram, it is **not** a fork/modded version. It does monkeypatching to add features to Pyrogram classes on the go (so i don't need to update on every Pyrogram's release).

## Usage
Import `pyromod` one time in your script and you'll already be able to use the modified pyrogram in all your handlers. Example:
```python
# config.py
import pyromod
from pyrogram import Client

app = Client('my_session')
```

Then you can, from another file, do `from config import app` to import the modded Pyrogram Client we created above. It will be modded globally.

All the patches are applied automatically as soon as pyromod is imported.

## Methods
All pyromod methods are callable by any of these ways:
- `await Client.<method>(identifier, ...)`
- `await Chat.<method>()`
- `await User.<method>()`

In the last two, Pyrogram automatically gets the ids from the object, to compound the `identifier` tuple that `Client.listen` uses.

These are the methods pyromod adds:
- `listen(identifier, filters=None, listener_type=ListenerTypes.MESSAGE, timeout=None, unallowed_click_alert=True)`
Awaits for a new message in the specified chat and returns its Message object. If listener_type is set to `ListenerTypes.CALLBACK_QUERY`, it awaits and returns a CallbackQuery object.
You can pass Update Filters to the `filters` parameter just like you do for the update handlers. e.g. `filters=filters.photo & filters.bot`
`identifier` is a tuple containing, in this exact order, (chat_id, user_id, message_id). It lets you specify exactly which update you want. You don't need to worry about that if you mostly use the bound methods.
`unnalowed_click_alert` is the text that users will see in an alert when the button is not waiting for them to click. If True, it uses the default text at `PyromodConfig.unnalowed_click_alert_text`. If False, no text is shown.

- `ask(text, identifier, filters=None, listener_type=ListenerTypes.MESSAGE, timeout=None, unallowed_click_alert=True)`
Same as `listen`, but sends a message to identifier[0] before and only then waits for a response.
You can additionally pass any of the `Client.send_message()` parameters. Check the example below.
The object of the sent message is returned inside of the attribute `request`

Example:
```python
answer = await message.chat.ask('*Send me your name:*', parse_mode=enums.ParseMode.MARKDOWN)
await answer.request.edit_text("Name received!")
await answer.reply(f'Your name is: {answer.text}', quote=True)    
```

- `Message.wait_for_click(from_user_id=None, timeout=None, filters=None, alert=True)`
Awaits from a click on any button on the Message object. If `from_user_id` is passed, pyromod will wait for a click of that user.
If you pass any text to `alert`, it will be shown to any other user. If `alert` is True, it will use the default text. If False, no text will be shown.

## `pyromod.helpers`
Tools for creating inline keyboards a lot easier.

### `pyromod.helpers.ikb`

Creates a inline keyboard.
Its first and only argument is a list (the keyboard itself) containing lists (the lines) of buttons, which can be lists or tuples. I use tuples to avoid a mess with a lot of brackets. Tuples makes it easier to read.

The button syntax is very simple: `(TEXT, VALUE, TYPE)`, with TYPE being any existent button type (e.g. `url`) and VALUE is its value. If you omit the type, it will be considered as a callback button.
If you pass only a string as button, it will be used as text and callback_data for the InlineKeyboardButton.
This syntax will be automagically converted by pyromod.

Examples:
```python
from pyromod.helpers import ikb
...
keyboard = ikb([
    [('Button 1', 'call_1'), ('Button 2', 'call_2')],
    [('Another button', 't.me/pyromodchat', 'url')]
])
await message.reply('Easy inline keyboard', reply_markup=keyboard)
```

```python
keyboard = ikb([
	["Mars", "Earth", "Venus"],
	["Saturn", "Jupyter"]
])
await message.reply("Easiest inline keyboard", reply_markup=keyboard)
```

- `pyromod.helpers.array_chunk`
Chunk the elements of a list into small lists. i.e. [1, 2, 3, 4] can become [[1,2], [3,4]]. This is extremely useful if you want to build a keyboard dinamically with more than 1 column. You just put all buttons together in a list and run:
```python
lines = array_chunk(buttons, 2)
keyboard = ikb(lines)
```
This will generate a list of lines with 2 buttons on each one.

### `pyromod.nav`
Tools for creating navigation keyboards.

- `pyromod.nav.Pagination`
Creates a full paginated keyboard. Usage:
```python
from pyrogram import Client, filters
from pyromod.nav import Pagination
from pyromod.helpers import ikb

def page_data(page):
    return f'view_page {page}'
def item_data(item, page):
    return f'view_item {item} {page}'
def item_title(item, page):
    return f'Item {item} of page {page}'

@Client.on_message(filters.regex('/nav'))
async def on_nav(c,m):
    objects = [*range(1,100)]
    page = Pagination(
        objects,
        page_data=page_data, # callback to define the callback_data for page buttons in the bottom
        item_data=item_data, # callback to define the callback_data for each item button
        item_title=item_title # callback to define the text for each item button
    )
    index = 0 # in which page is it now? (used to calculate the offset)
    lines = 5 # how many lines of the keyboard to include for the items
    columns = how many columns include in each items' line
    kb = page.create(index, lines, columns)
    await m.reply('Test', reply_markup=ikb(kb))
```

## pyromod.PyrogramConfig
It lets you do some tweaks on pyromod behavior.
```python
class PyromodConfig:
    timeout_handler = None
    stopped_handler = None
    throw_exceptions = True
    unallowed_click_alert = True
    unallowed_click_alert_text = (
        "[pyromod] You're not expected to click this button."
    )
```
`timeout_handler` and `stopped_handler` are callbacks that receive (identifier, listener_data) as arguments. timeout_handler receives an extra arg `timeout`. When they are in use, pyromod won't throw the exceptions ListenerStopped and ListenedTimeout.

### Copyright & License
This project may include snippets of Pyrogram code
- Pyrogram - Telegram MTProto API Client Library for Python. Copyright (C) 2017-2022 Dan <<https://github.com/delivrance>>

Licensed under the terms of the [GNU Lesser General Public License v3 or later (LGPLv3+)](COPYING.lesser)




%prep
%autosetup -n pyromod-2.0.0

%build
%py3_build

%install
%py3_install
install -d -m755 %{buildroot}/%{_pkgdocdir}
if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
pushd %{buildroot}
if [ -d usr/lib ]; then
	find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/lib64 ]; then
	find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/bin ]; then
	find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/sbin ]; then
	find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
fi
touch doclist.lst
if [ -d usr/share/man ]; then
	find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
fi
popd
mv %{buildroot}/filelist.lst .
mv %{buildroot}/doclist.lst .

%files -n python3-pyromod -f filelist.lst
%dir %{python3_sitelib}/*

%files help -f doclist.lst
%{_docdir}/*

%changelog
* Tue Apr 11 2023 Python_Bot <Python_Bot@openeuler.org> - 2.0.0-1
- Package Spec generated