smiley.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #!/usr/bin/env python3
  2. # -*- coding: UTF-8 -*-
  3. import os
  4. import re
  5. import re
  6. import json
  7. from .common.textutil import get_file_b64
  8. STATIC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
  9. TENCENT_SMILEY_FILE = os.path.join(STATIC_PATH, 'tencent-smiley.json')
  10. try:
  11. UNICODE_SMILEY_RE = re.compile(
  12. u'[\U00010000-\U0010ffff]|[\u2600-\u2764]|\u2122|\u00a9|\u00ae|[\ue000-\ue5ff]'
  13. )
  14. except re.error:
  15. # UCS-2 build
  16. UNICODE_SMILEY_RE = re.compile(
  17. u'[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u2600-\u2764]|\u2122|\u00a9|\u00ae|[\ue000-\ue5ff]'
  18. )
  19. HEAD = """.smiley {
  20. padding: 1px;
  21. background-position: -1px -1px;
  22. background-repeat: no-repeat;
  23. width: 20px;
  24. height: 20px;
  25. display: inline-block;
  26. vertical-align: top;
  27. zoom: 1;
  28. }
  29. """
  30. TEMPLATE = """.{name} {{
  31. background-image: url("data:image/png;base64,{b64}");
  32. background-size: 24px 24px;
  33. }}"""
  34. def _css_class_name(name: str) -> str:
  35. """
  36. Sanitizes a string to be a valid CSS class name by replacing invalid characters with underscores.
  37. """
  38. # CSS class names must start with a letter or underscore, followed by letters, digits, hyphens, or underscores
  39. name = re.sub(r'[^a-zA-Z0-9_-]', '_', name)
  40. # Ensure the first character is a valid start character (letter or underscore)
  41. if not name[0].isalpha() and name[0] != '_':
  42. name = '_' + name
  43. return "smiley_" + name
  44. class SmileyProvider(object):
  45. def __init__(self, html_replace=True):
  46. """ html_replace: replace smileycode by html.
  47. otherwise, replace by plain text
  48. """
  49. self.html_replace = html_replace
  50. if not html_replace:
  51. raise NotImplementedError()
  52. # [微笑] -> smiley/0.png
  53. self.tencent_smiley = json.load(open(TENCENT_SMILEY_FILE))
  54. self.used_smileys = set()
  55. def reset(self):
  56. self.used_smileys.clear()
  57. def gen_replace_elem(self, smiley_path):
  58. self.used_smileys.add(str(smiley_path))
  59. return '<span class="smiley {}"></span>'.format(_css_class_name(smiley_path))
  60. def replace_smileycode(self, msg):
  61. """ replace the smiley code in msg
  62. return a html
  63. """
  64. # pre-filter:
  65. if ('[' not in msg) and ('/' not in msg) and not UNICODE_SMILEY_RE.findall(msg):
  66. return msg
  67. for k, v in self.tencent_smiley.items():
  68. if k in msg:
  69. msg = msg.replace(k, self.gen_replace_elem(v))
  70. return msg
  71. def gen_used_smiley_css(self):
  72. ret = HEAD
  73. for path in self.used_smileys:
  74. fname = os.path.join(STATIC_PATH, path)
  75. b64 = get_file_b64(fname)
  76. ret = ret + TEMPLATE.format(name=_css_class_name(path), b64=b64)
  77. return ret
  78. if __name__ == '__main__':
  79. smiley = SmileyProvider()
  80. msg = u"[天啊]哈哈呵呵hihi\U0001f684\u2728\u0001 /::<\ue415"
  81. msg = smiley.replace_smileycode(msg)
  82. print(msg)
  83. print()
  84. print(smiley.gen_used_smiley_css())