You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
3.5 KiB

  1. # Copyright (c) 2020 Tulir Asokan
  2. #
  3. # This Source Code Form is subject to the terms of the Mozilla Public
  4. # License, v. 2.0. If a copy of the MPL was not distributed with this
  5. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. from hashlib import sha256
  7. import argparse
  8. import os.path
  9. import asyncio
  10. import string
  11. import json
  12. import magic
  13. from .lib import matrix, util
  14. def convert_name(name: str) -> str:
  15. name_translate = {
  16. ord(" "): ord("_"),
  17. }
  18. allowed_chars = string.ascii_letters + string.digits + "_-/.#"
  19. return "".join(filter(lambda char: char in allowed_chars, name.translate(name_translate)))
  20. async def main(args: argparse.Namespace) -> None:
  21. await matrix.load_config(args.config)
  22. dirname = os.path.basename(os.path.abspath(args.path))
  23. meta_path = os.path.join(args.path, "pack.json")
  24. try:
  25. with open(meta_path) as pack_file:
  26. pack = json.load(pack_file)
  27. print(f"Loaded existing pack meta from {meta_path}")
  28. except FileNotFoundError:
  29. pack = {
  30. "title": args.title or dirname,
  31. "id": args.id or convert_name(dirname),
  32. "stickers": [],
  33. }
  34. old_stickers = {}
  35. else:
  36. old_stickers = {sticker["id"]: sticker for sticker in pack["stickers"]}
  37. pack["stickers"] = []
  38. for file in sorted(os.listdir(args.path)):
  39. if file.startswith("."):
  40. continue
  41. path = os.path.join(args.path, file)
  42. if not os.path.isfile(path):
  43. continue
  44. mime = magic.from_file(path, mime=True)
  45. if not mime.startswith("image/"):
  46. continue
  47. try:
  48. with open(path, "rb") as image_file:
  49. image_data = image_file.read()
  50. except Exception as e:
  51. print(f"Failed to read {file}: {e}")
  52. continue
  53. print(f"Processing {file}", end="", flush=True)
  54. name = os.path.splitext(file)[0]
  55. # If the name starts with "number-", remove the prefix
  56. name_split = name.split("-", 1)
  57. if len(name_split) == 2 and name_split[0].isdecimal():
  58. name = name_split[1]
  59. sticker_id = f"sha256:{sha256(image_data).hexdigest()}"
  60. print(".", end="", flush=True)
  61. if sticker_id in old_stickers:
  62. pack["stickers"].append({
  63. **old_stickers[sticker_id],
  64. "body": name,
  65. })
  66. print(f".. using existing upload")
  67. else:
  68. image_data, width, height = util.convert_image(image_data)
  69. print(".", end="", flush=True)
  70. mxc = await matrix.upload(image_data, "image/png", file)
  71. print(".", end="", flush=True)
  72. sticker = util.make_sticker(mxc, width, height, len(image_data), name)
  73. sticker["id"] = sticker_id
  74. pack["stickers"].append(sticker)
  75. print(" uploaded", flush=True)
  76. with open(meta_path, "w") as pack_file:
  77. json.dump(pack, pack_file)
  78. print(f"Wrote pack to {meta_path}")
  79. parser = argparse.ArgumentParser()
  80. parser.add_argument("--config",
  81. help="Path to JSON file with Matrix homeserver and access_token",
  82. type=str, default="config.json")
  83. parser.add_argument("--title", help="Override the sticker pack displayname", type=str)
  84. parser.add_argument("--id", help="Override the sticker pack ID", type=str)
  85. parser.add_argument("path", help="Path to the sticker pack directory", type=str)
  86. def cmd():
  87. asyncio.get_event_loop().run_until_complete(main(parser.parse_args()))
  88. if __name__ == "__main__":
  89. cmd()