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.
- ifa_nextcloud-0.2.2/PKG-INFO +663 -0
- ifa_nextcloud-0.2.2/README.md +640 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/pyproject.toml +1 -1
- ifa_nextcloud-0.2.2/src/__init__.py +25 -0
- ifa_nextcloud-0.2.2/src/nextcloud/bot/__init__.py +9 -0
- ifa_nextcloud-0.2.0/PKG-INFO +0 -561
- ifa_nextcloud-0.2.0/README.md +0 -538
- ifa_nextcloud-0.2.0/src/__init__.py +0 -12
- ifa_nextcloud-0.2.0/src/nextcloud/bot/__init__.py +0 -6
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/.github/workflows/publish-to-pypi.yml +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/LICENSE +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/reqirements.txt +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/__init__.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/async_bot.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/bot.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/core/__init__.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/core/bot_core.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/core/models.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/http/__init__.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/http/async_.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/http/base.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/bot/http/sync.py +0 -0
- {ifa_nextcloud-0.2.0 → ifa_nextcloud-0.2.2}/src/nextcloud/email/__init__.py +0 -0
- {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
|
+
```
|