ifa-nextcloud 0.2.0__tar.gz → 0.2.2__tar.gz

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.
Files changed (24) hide show
  1. ifa_nextcloud-0.2.2/PKG-INFO +663 -0
  2. ifa_nextcloud-0.2.2/README.md +640 -0
  3. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/pyproject.toml +1 -1
  4. ifa_nextcloud-0.2.2/src/__init__.py +25 -0
  5. ifa_nextcloud-0.2.2/src/nextcloud/bot/__init__.py +9 -0
  6. ifa_nextcloud-0.2.0/PKG-INFO +0 -561
  7. ifa_nextcloud-0.2.0/README.md +0 -538
  8. ifa_nextcloud-0.2.0/src/__init__.py +0 -12
  9. ifa_nextcloud-0.2.0/src/nextcloud/bot/__init__.py +0 -6
  10. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/.github/workflows/publish-to-pypi.yml +0 -0
  11. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/LICENSE +0 -0
  12. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/reqirements.txt +0 -0
  13. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/__init__.py +0 -0
  14. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/async_bot.py +0 -0
  15. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/bot.py +0 -0
  16. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/core/__init__.py +0 -0
  17. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/core/bot_core.py +0 -0
  18. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/core/models.py +0 -0
  19. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/http/__init__.py +0 -0
  20. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/http/async_.py +0 -0
  21. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/http/base.py +0 -0
  22. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/http/sync.py +0 -0
  23. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/email/__init__.py +0 -0
  24. {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/email/client.py +0 -0
@@ -0,0 +1,663 @@
1
+ Metadata-Version: 2.4
2
+ Name: ifa_nextcloud
3
+ Version: 0.2.2
4
+ Summary: Python библиотека для создания ботов в Nextcloud Talk с API, похожим на python-telegram-bot
5
+ Project-URL: Homepage, https://github.com/1FaKe110/nextcloud
6
+ Project-URL: Repository, https://github.com/1FaKe110/nextcloud.git
7
+ Project-URL: Issues, https://github.com/1FaKe110/nextcloud/issues
8
+ Author-email: ifake <your@email.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: bot,chat,ifa,messaging,nextcloud,talk
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: aiohttp>=3.9.0
20
+ Requires-Dist: loguru>=0.6.0
21
+ Requires-Dist: requests>=2.25.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ ```markdown
25
+ # Nextcloud Talk Bot
26
+
27
+ Python library for creating bots in Nextcloud Talk with an API similar to python-telegram-bot.
28
+
29
+ ## Features
30
+
31
+ - Simple API inspired by python-telegram-bot
32
+ - Send text messages with Markdown support
33
+ - Send files (documents, photos, any attachments)
34
+ - Automatic membership management in rooms
35
+ - Message threading and reply support
36
+ - Automatic API version detection (v1-v4)
37
+ - Automatic re-authentication on errors
38
+ - Polling mode for receiving messages
39
+ - Both sync and async versions available
40
+ - Multi-room support (async version)
41
+
42
+ ## Requirements
43
+
44
+ - Python 3.10+
45
+ - Nextcloud 33.x or higher (with Talk app installed)
46
+ - Bot account in Nextcloud (app token recommended)
47
+
48
+ ## Installation
49
+
50
+ ### From source
51
+
52
+ ```bash
53
+ git clone https://github.com/your-username/nextcloud-talk-bot.git
54
+ cd nextcloud-talk-bot
55
+ pip install -r requirements.txt
56
+ ```
57
+
58
+ ### As a library
59
+
60
+ Copy the `nextcloud` folder to your project or install as module:
61
+
62
+ ```python
63
+ from nextcloud import Bot # sync version
64
+ # or
65
+ from nextcloud import AsyncBot # async version
66
+ ```
67
+
68
+ ## Quick Start
69
+
70
+ ### Minimal example (sync)
71
+
72
+ ```python
73
+ from nextcloud import Bot
74
+
75
+ # Create bot
76
+ bot = Bot(
77
+ host="https://nextcloud.example.com",
78
+ user="bot_user",
79
+ password="your-app-token",
80
+ default_room="room_token_here"
81
+ )
82
+
83
+ # Send message
84
+ bot.send_message("Hello, World!")
85
+
86
+ # Start polling
87
+ bot.run_polling()
88
+ ```
89
+
90
+ ### Minimal example (async)
91
+
92
+ ```python
93
+ from nextcloud import AsyncBot
94
+ import asyncio
95
+
96
+ bot = AsyncBot(
97
+ host="https://nextcloud.example.com",
98
+ user="bot_user",
99
+ password="your-app-token",
100
+ default_room="room_token_here"
101
+ )
102
+
103
+ @bot.command("start")
104
+ async def start_command(update, context):
105
+ await update.message.reply_text("Hello from async bot!")
106
+
107
+ asyncio.run(bot.run_polling())
108
+ ```
109
+
110
+ ## Sending Messages
111
+
112
+ ### Text messages
113
+
114
+ ```python
115
+ # Simple text
116
+ bot.send_message("Hello, World!")
117
+
118
+ # Markdown formatting
119
+ bot.send_message("**Bold text** and *italic*")
120
+
121
+ # Reply to a message
122
+ bot.send_message(
123
+ chat_id="room_token",
124
+ text="This is a reply",
125
+ reply_to_message_id=12345
126
+ )
127
+ ```
128
+
129
+ ### Sending files
130
+
131
+ ```python
132
+ # Send file with caption
133
+ bot.send_message(
134
+ chat_id="room_token",
135
+ text="Check this document",
136
+ file_path="/path/to/document.pdf"
137
+ )
138
+
139
+ # Send photo
140
+ bot.send_photo(
141
+ "room_token",
142
+ "/path/to/photo.jpg",
143
+ caption="Beautiful sunset"
144
+ )
145
+
146
+ # Send document
147
+ bot.send_document(
148
+ "room_token",
149
+ "/path/to/report.pdf",
150
+ caption="Monthly report"
151
+ )
152
+
153
+ # Send file from memory
154
+ file_content = b"Hello, this is a text file!"
155
+ bot.send_message(
156
+ chat_id="room_token",
157
+ text="Generated file",
158
+ file_content=file_content,
159
+ file_name="hello.txt",
160
+ mime_type="text/plain"
161
+ )
162
+
163
+ # Send file from BytesIO
164
+ from io import BytesIO
165
+
166
+ buffer = BytesIO()
167
+ buffer.write(b"Some data")
168
+ buffer.seek(0)
169
+
170
+ bot.send_document(
171
+ "room_token",
172
+ buffer,
173
+ caption="Data from buffer"
174
+ )
175
+ ```
176
+
177
+ ## Receiving Messages
178
+
179
+ ### Polling mode
180
+
181
+ ```python
182
+ # Start polling (sync version)
183
+ bot.run_polling(chat_id="room_token", poll_interval=2)
184
+
185
+ # Parameters:
186
+ # - chat_id: room token (uses default_room if not specified)
187
+ # - poll_interval: polling interval in seconds (default 2)
188
+ ```
189
+
190
+ ### Async polling with multi-room support
191
+
192
+ ```python
193
+ from nextcloud import AsyncBot
194
+ import asyncio
195
+
196
+ # Multi-room mode - listens to all rooms where bot is member
197
+ bot = AsyncBot(
198
+ host="https://nextcloud.example.com",
199
+ user="bot_user",
200
+ password="app-token",
201
+ listen_all_rooms=True,
202
+ max_concurrent_rooms=50
203
+ )
204
+
205
+ @bot.message_handler
206
+ async def handle_all_messages(update, context):
207
+ print(f"Message from {update.message.chat.id}: {update.message.text}")
208
+
209
+ asyncio.run(bot.run_polling()) # No room token needed
210
+ ```
211
+
212
+ ### Message object
213
+
214
+ When receiving a message, you get a `Message` object with these attributes:
215
+
216
+ ```python
217
+ @bot.message_handler
218
+ def handle_message(update, context):
219
+ message = update.message
220
+
221
+ # Basic attributes
222
+ print(f"ID: {message.message_id}")
223
+ print(f"Text: {message.text}")
224
+ print(f"From: {message.from_user.full_name}")
225
+ print(f"Time: {message.date}")
226
+
227
+ # Reply to message
228
+ message.reply_text("Got your message!")
229
+
230
+ # Or use reply alias
231
+ message.reply("Same thing")
232
+ ```
233
+
234
+ ## Command Handling
235
+
236
+ ### Sync version
237
+
238
+ ```python
239
+ from nextcloud import Bot
240
+
241
+ bot = Bot(
242
+ host="https://nextcloud.example.com",
243
+ user="my_bot",
244
+ password="app-token",
245
+ default_room="room_token"
246
+ )
247
+
248
+ @bot.command("start")
249
+ def start_command(update, context):
250
+ update.message.reply_text("Welcome! I am a Nextcloud Talk bot!")
251
+
252
+ @bot.command("echo")
253
+ def echo_command(update, context):
254
+ text = update.message.text.replace("/echo", "").strip()
255
+ if text:
256
+ update.message.reply_text(f"Echo: {text}")
257
+ else:
258
+ update.message.reply_text("Write something after /echo")
259
+
260
+ @bot.message_handler
261
+ def handle_all(update, context):
262
+ message = update.message
263
+ if message.text:
264
+ print(f"Received: {message.text}")
265
+
266
+ if __name__ == "__main__":
267
+ bot.run_polling()
268
+ ```
269
+
270
+ ### Async version with sync/async handlers
271
+
272
+ ```python
273
+ from nextcloud import AsyncBot
274
+ import asyncio
275
+
276
+ bot = AsyncBot(
277
+ host="https://nextcloud.example.com",
278
+ user="my_bot",
279
+ password="app-token",
280
+ default_room="room_token"
281
+ )
282
+
283
+ # Async handler
284
+ @bot.command("time")
285
+ async def time_command(update, context):
286
+ from datetime import datetime
287
+ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
288
+ await update.message.reply_text(f"Current time: {now}")
289
+
290
+ # Sync handler (runs in thread pool)
291
+ @bot.command("ping")
292
+ def ping_command(update, context):
293
+ update.message.reply_text("pong!")
294
+
295
+ asyncio.run(bot.run_polling())
296
+ ```
297
+
298
+ ## Room Management
299
+
300
+ ### Joining rooms
301
+
302
+ ```python
303
+ # Automatic joining on startup (default)
304
+ bot = Bot(
305
+ host="https://nextcloud.example.com",
306
+ user="bot_user",
307
+ password="password",
308
+ default_room="room_token",
309
+ auto_join_room=True
310
+ )
311
+
312
+ # Manual join
313
+ bot.join_room("room_token", password="room_password")
314
+ ```
315
+
316
+ ### Getting room information
317
+
318
+ ```python
319
+ # List all available rooms
320
+ rooms = bot.get_rooms()
321
+ for room in rooms:
322
+ print(f"Room: {room.get('name')} ({room.get('token')})")
323
+ print(f"Participants: {room.get('participantCount')}")
324
+
325
+ # Get specific room info
326
+ room_info = bot.get_room_info("room_token")
327
+ print(f"Name: {room_info.get('name')}")
328
+ print(f"Type: {room_info.get('type')}")
329
+
330
+ # Get participants list
331
+ participants = bot.get_room_participants("room_token")
332
+ for p in participants:
333
+ print(f"Participant: {p.get('actorDisplayName')}")
334
+ ```
335
+
336
+ ## File Download
337
+
338
+ ```python
339
+ @bot.message_handler
340
+ def handle_file(update, context):
341
+ message = update.message
342
+
343
+ if message.files:
344
+ for file_obj in message.files:
345
+ # Download to memory
346
+ content = bot.download_file(file_obj)
347
+ print(f"Downloaded {file_obj.file_name}: {len(content)} bytes")
348
+
349
+ # Download to disk
350
+ path = bot.download_file(file_obj, f"/tmp/{file_obj.file_name}")
351
+ print(f"Saved to {path}")
352
+ ```
353
+
354
+ ## Diagnostic Tools
355
+
356
+ ### Connection check
357
+
358
+ ```python
359
+ # Check session status
360
+ status = bot.check_session_status()
361
+ if status['authenticated']:
362
+ print(f"Connected as: {status['user']}")
363
+ else:
364
+ print("Authentication failed")
365
+
366
+ # Room access diagnosis
367
+ diagnostic = bot.diagnose_room_access("room_token")
368
+ import json
369
+ print(json.dumps(diagnostic, indent=2))
370
+ ```
371
+
372
+ ### Bot info
373
+
374
+ ```python
375
+ # Get bot information
376
+ info = bot.get_bot_info()
377
+ print(f"User: {info['user']}")
378
+ print(f"Host: {info['host']}")
379
+ print(f"Authenticated: {info['authenticated']}")
380
+ print(f"Rooms count: {info['rooms_count']}")
381
+ ```
382
+
383
+ ## Configuration
384
+
385
+ ### Bot parameters
386
+
387
+ | Parameter | Type | Default | Description |
388
+ |-----------|------|---------|-------------|
389
+ | host | str | - | Nextcloud server URL |
390
+ | user | str | - | Bot username |
391
+ | password | str | - | Password or app token |
392
+ | default_room | str | None | Default room token |
393
+ | read_all_chat | bool | False | Read full history (old to new) or only new messages |
394
+ | auto_join_room | bool | True | Automatically join rooms |
395
+ | listen_all_rooms | bool | False | (Async only) Listen to all rooms where bot is member |
396
+ | max_concurrent_rooms | int | 50 | (Async multi-room only) Max concurrent rooms to poll |
397
+
398
+ ### Logging setup
399
+
400
+ The library uses `loguru` for logging. You can configure log level:
401
+
402
+ ```python
403
+ from loguru import logger
404
+ import sys
405
+
406
+ # Set log level
407
+ logger.remove() # Remove default handler
408
+ logger.add(sys.stderr, level="INFO") # Only INFO and above
409
+ # or
410
+ logger.add(sys.stderr, level="DEBUG") # Detailed logging
411
+ ```
412
+
413
+ ## Examples
414
+
415
+ ### Echo bot
416
+
417
+ ```python
418
+ from nextcloud import Bot
419
+
420
+ bot = Bot(
421
+ host="https://nextcloud.example.com",
422
+ user="echo_bot",
423
+ password="app-token",
424
+ default_room="room_token"
425
+ )
426
+
427
+ @bot.message_handler
428
+ def echo(update, context):
429
+ message = update.message
430
+ if message.text:
431
+ message.reply_text(f"Echo: {message.text}")
432
+ else:
433
+ message.reply_text("Write some text and I'll echo it!")
434
+
435
+ bot.run_polling()
436
+ ```
437
+
438
+ ### Notification bot
439
+
440
+ ```python
441
+ from nextcloud import Bot
442
+
443
+ class NotificationBot:
444
+ def __init__(self, host, user, password, room_token):
445
+ self.bot = Bot(
446
+ host=host,
447
+ user=user,
448
+ password=password,
449
+ default_room=room_token
450
+ )
451
+ self.room = room_token
452
+
453
+ def send_notification(self, title, message, priority="info"):
454
+ emoji = {
455
+ "info": "[i]",
456
+ "success": "[OK]",
457
+ "warning": "[!]",
458
+ "error": "[X]"
459
+ }.get(priority, "[*]")
460
+
461
+ text = f"{emoji} {title}\n{message}"
462
+ self.bot.send_message(self.room, text)
463
+
464
+ def send_file_notification(self, title, file_path, message=None):
465
+ full_text = f"[FILE] {title}"
466
+ if message:
467
+ full_text += f"\n{message}"
468
+
469
+ self.bot.send_message(
470
+ chat_id=self.room,
471
+ text=full_text,
472
+ file_path=file_path
473
+ )
474
+
475
+ # Usage
476
+ notifier = NotificationBot(
477
+ host="https://nextcloud.example.com",
478
+ user="notifier",
479
+ password="app-token",
480
+ room_token="room_token"
481
+ )
482
+
483
+ notifier.send_notification("Backup", "Backup completed", "success")
484
+ notifier.send_file_notification("Report", "/tmp/report.pdf", "Monthly report ready")
485
+ ```
486
+
487
+ ### Multi-room bot (async)
488
+
489
+ ```python
490
+ from nextcloud import AsyncBot
491
+ import asyncio
492
+
493
+ bot = AsyncBot(
494
+ host="https://nextcloud.example.com",
495
+ user="helper_bot",
496
+ password="app-token",
497
+ listen_all_rooms=True,
498
+ auto_join_room=True
499
+ )
500
+
501
+ # Shared storage for all rooms
502
+ user_notes = {}
503
+
504
+ @bot.command("note")
505
+ async def save_note(update, context):
506
+ user_id = update.message.from_user.id
507
+ room_id = update.message.chat.id
508
+ text = update.message.text.replace("/note", "").strip()
509
+
510
+ if text:
511
+ key = f"{room_id}_{user_id}"
512
+ user_notes[key] = text
513
+ await update.message.reply_text("Note saved!")
514
+ else:
515
+ await update.message.reply_text("Write something after /note")
516
+
517
+ @bot.command("mynote")
518
+ async def get_note(update, context):
519
+ user_id = update.message.from_user.id
520
+ room_id = update.message.chat.id
521
+ key = f"{room_id}_{user_id}"
522
+ note = user_notes.get(key)
523
+
524
+ if note:
525
+ await update.message.reply_text(f"Your note:\n{note}")
526
+ else:
527
+ await update.message.reply_text("You have no saved notes")
528
+
529
+ @bot.command("rooms")
530
+ async def list_rooms(update, context):
531
+ rooms = await bot.get_rooms()
532
+ room_list = "\n".join([f"- {r.get('name')}" for r in rooms[:10]])
533
+ await update.message.reply_text(f"Available rooms:\n{room_list}")
534
+
535
+ asyncio.run(bot.run_polling())
536
+ ```
537
+
538
+ ### Async bot with context manager
539
+
540
+ ```python
541
+ from nextcloud import AsyncBot
542
+ import asyncio
543
+
544
+ async def main():
545
+ async with AsyncBot(
546
+ host="https://nextcloud.example.com",
547
+ user="my_bot",
548
+ password="app-token",
549
+ default_room="room_token"
550
+ ) as bot:
551
+
552
+ @bot.command("ping")
553
+ async def ping(update, context):
554
+ await update.message.reply_text("pong!")
555
+
556
+ await bot.run_polling()
557
+
558
+ asyncio.run(main())
559
+ ```
560
+
561
+ ## Troubleshooting
562
+
563
+ ### Authentication error
564
+
565
+ ```python
566
+ # Check connection
567
+ status = bot.check_session_status()
568
+ if not status['authenticated']:
569
+ print(f"Error: {status.get('error')}")
570
+ print("Check username and password/app token")
571
+ ```
572
+
573
+ ### Room not found
574
+
575
+ ```python
576
+ # Get list of available rooms
577
+ rooms = bot.get_rooms()
578
+ print("Available rooms:")
579
+ for room in rooms:
580
+ print(f" - {room.get('name')} (token: {room.get('token')})")
581
+ ```
582
+
583
+ ### File upload issues
584
+
585
+ ```python
586
+ # Use diagnostics
587
+ result = bot.diagnose_room_access("room_token")
588
+ import json
589
+ print(json.dumps(result, indent=2))
590
+
591
+ # Check WebDAV write permissions
592
+ # Ensure bot has permission to upload files
593
+ ```
594
+
595
+ ## API Reference
596
+
597
+ ### Bot (sync) / AsyncBot (async)
598
+
599
+ #### Sending methods
600
+
601
+ | Method | Description |
602
+ |--------|-------------|
603
+ | `send_message(chat_id, text, ...)` | Universal send method (text/files) |
604
+ | `send_file(chat_id, file_path, caption, ...)` | Send file |
605
+ | `send_photo(chat_id, photo, caption, ...)` | Send photo |
606
+ | `send_document(chat_id, document, caption, ...)` | Send document |
607
+ | `reply_to(message, text, ...)` | Reply to a message |
608
+
609
+ #### Room management
610
+
611
+ | Method | Description |
612
+ |--------|-------------|
613
+ | `join_room(chat_id, password)` | Join a room |
614
+ | `get_rooms()` | Get list of all rooms |
615
+ | `get_room_info(chat_id)` | Get room information |
616
+ | `get_room_participants(chat_id)` | Get participants list |
617
+
618
+ #### Diagnostics
619
+
620
+ | Method | Description |
621
+ |--------|-------------|
622
+ | `check_session_status()` | Check session status |
623
+ | `diagnose_room_access(chat_id)` | Diagnose room access |
624
+ | `get_bot_info()` | Get bot information |
625
+
626
+ ### Message object
627
+
628
+ | Attribute/Method | Description |
629
+ |------------------|-------------|
630
+ | `message_id` | Message ID |
631
+ | `text` | Message text |
632
+ | `from_user` | User object (sender) |
633
+ | `chat` | Chat object (room) |
634
+ | `date` | Send time |
635
+ | `files` | List of File objects |
636
+ | `reply_to_message` | Replied message (if any) |
637
+ | `reply_text(text)` | Reply to message |
638
+ | `reply(text)` | Alias for reply_text |
639
+
640
+ ### File object
641
+
642
+ | Attribute | Description |
643
+ |-----------|-------------|
644
+ | `file_id` | File ID |
645
+ | `file_name` | File name |
646
+ | `file_size` | File size in bytes |
647
+ | `mime_type` | MIME type |
648
+ | `file_path` | Path on server |
649
+ | `download_url` | Download URL |
650
+
651
+ ## License
652
+
653
+ MIT License
654
+
655
+ ## Contributing
656
+
657
+ Pull requests and issues are welcome!
658
+
659
+ ## Links
660
+
661
+ - Nextcloud Talk API Documentation: https://nextcloud-talk.readthedocs.io/
662
+ - Nextcloud Developer Documentation: https://docs.nextcloud.com/server/latest/developer_manual/
663
+ ```