25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

81 lines
2.2 KiB

  1. #!/usr/bin/env python3
  2. import asyncio
  3. import base64
  4. import datetime
  5. import functools
  6. import json
  7. import os
  8. import re
  9. import sys
  10. import telethon
  11. try:
  12. import tqdm
  13. except ImportError:
  14. tqdm = None
  15. API_ID = os.environ['TELEGRAM_API_ID']
  16. API_HASH = os.environ['TELEGRAM_API_HASH']
  17. BOT_TOKEN = os.environ['TELEGRAM_BOT_TOKEN']
  18. URL_PATTERN = re.compile(r'^https?://t\.me/(?:s/)?(?P<channel>[^/]+)/(?P<message>\d+)$')
  19. def stuff_to_json(o):
  20. if isinstance(o, datetime.datetime):
  21. return o.isoformat()
  22. if isinstance(o, bytes):
  23. return f'binary data: {base64.b64encode(o).decode("ascii")}'
  24. raise TypeError(f'Object of type {type(o)} is not JSON serializable')
  25. def download_callback(current, total, bar = None):
  26. if bar is None:
  27. return
  28. bar.total = total #FIXME: Accesses undocumented attribute of tqdm
  29. bar.update(current - bar.n) #FIXME: https://github.com/tqdm/tqdm/issues/1264
  30. async def main():
  31. # Parse URLs
  32. targets = []
  33. for url in sys.argv[1:]:
  34. m = URL_PATTERN.match(url)
  35. if not m:
  36. print(f'Error: {url} is not a recognised Telegram URL', file = sys.stderr)
  37. sys.exit(1)
  38. targets.append((m['channel'], int(m['message'])))
  39. if not targets:
  40. print(f'Usage: telegram-dl.py URL [URL...]', file = sys.stderr)
  41. sys.exit(1)
  42. channelName = targets[0][0]
  43. if not all(x[0] == channelName for x in targets[1:]):
  44. print(f'Error: all URLs must be of the same channel', file = sys.stderr)
  45. sys.exit(1)
  46. ids = [x[1] for x in targets]
  47. # Let's go...
  48. client = telethon.TelegramClient('.telegram-dl', API_ID, API_HASH)
  49. print('Connecting', file = sys.stderr)
  50. await client.start(bot_token = BOT_TOKEN)
  51. print('Fetching messages', file = sys.stderr)
  52. messages = await client.get_messages(channelName, ids = ids)
  53. for message in messages:
  54. if not message:
  55. continue
  56. print(f'Processing message {message.id}', file = sys.stderr)
  57. with open(f'{channelName}_{message.id}.json', 'x') as fp:
  58. json.dump(message.to_dict(), fp, default = stuff_to_json)
  59. if message.media and tqdm:
  60. bar = tqdm.tqdm(unit = 'iB', unit_divisor = 1024, unit_scale = True)
  61. else:
  62. bar = None
  63. try:
  64. await client.download_media(message, progress_callback = functools.partial(download_callback, bar = bar))
  65. finally:
  66. if bar is not None:
  67. bar.close()
  68. asyncio.run(main())