smiley.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #!/usr/bin/env python3
  2. # -*- coding: UTF-8 -*-
  3. import os
  4. import re
  5. import json
  6. import struct
  7. from common.textutil import get_file_b64
  8. STATIC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
  9. UNICODE_SMILEY_FILE = os.path.join(STATIC_PATH, 'unicode-smiley.json')
  10. TENCENT_SMILEY_FILE = os.path.join(STATIC_PATH, 'tencent-smiley.json')
  11. TENCENT_EXTRASMILEY_FILE = os.path.join(STATIC_PATH, 'tencent-smiley-extra.json')
  12. try:
  13. UNICODE_SMILEY_RE = re.compile(
  14. u'[\U00010000-\U0010ffff]|[\u2600-\u2764]|\u2122|\u00a9|\u00ae|[\ue000-\ue5ff]'
  15. )
  16. except re.error:
  17. # UCS-2 build
  18. UNICODE_SMILEY_RE = re.compile(
  19. u'[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u2600-\u2764]|\u2122|\u00a9|\u00ae|[\ue000-\ue5ff]'
  20. )
  21. HEAD = """.smiley {
  22. padding: 1px;
  23. background-position: -1px -1px;
  24. background-repeat: no-repeat;
  25. width: 20px;
  26. height: 20px;
  27. display: inline-block;
  28. vertical-align: top;
  29. zoom: 1;
  30. }
  31. """
  32. TEMPLATE = """.smiley{name} {{
  33. background-image: url("data:image/png;base64,{b64}");
  34. }}"""
  35. class SmileyProvider(object):
  36. def __init__(self, html_replace=True):
  37. """ html_replace: replace smileycode by html.
  38. otherwise, replace by plain text
  39. """
  40. self.html_replace = html_replace
  41. if not html_replace:
  42. raise NotImplementedError()
  43. # [微笑] -> 0
  44. self.tencent_smiley = json.load(open(TENCENT_SMILEY_FILE))
  45. # some extra smiley from javascript on wx.qq.com
  46. extra_smiley = json.load(open(TENCENT_EXTRASMILEY_FILE))
  47. extra_smiley = {u'[' + k + u']': v for k, v in
  48. extra_smiley.items()}
  49. self.tencent_smiley.update(extra_smiley)
  50. # 1f35c -> "\ue340"
  51. #self.unicode_smiley_code = gUnicodeCodeMap
  52. # u'\U0001f35c' -> "e340" # for iphone
  53. # u'\ue415' -> 'e415' # for android
  54. unicode_smiley_dict = json.load(open(UNICODE_SMILEY_FILE))
  55. self.unicode_smiley = {(self.unichar(int(k, 16))): hex(ord(v))[2:] for k, v in
  56. unicode_smiley_dict.items()}
  57. self.unicode_smiley.update({v: hex(ord(v))[2:] for _, v in
  58. unicode_smiley_dict.items()})
  59. self.used_smiley_id = set()
  60. def unichar(self, i):
  61. try:
  62. return chr(i)
  63. except ValueError:
  64. return struct.pack('i', i).decode('utf-32')
  65. def gen_replace_elem(self, smiley_id):
  66. self.used_smiley_id.add(str(smiley_id))
  67. return '<span class="smiley smiley{}"></span>'.format(smiley_id)
  68. def _replace_unicode(self, msg):
  69. if not UNICODE_SMILEY_RE.findall(msg):
  70. # didn't find the code
  71. return msg
  72. for k, v in self.unicode_smiley.items():
  73. if k in msg:
  74. msg = msg.replace(k, self.gen_replace_elem(v))
  75. return msg
  76. def _replace_tencent(self, msg):
  77. if (not '[' in msg or not ']' in msg) \
  78. and (not '/:' in msg) and (not '/' in msg):
  79. return msg
  80. for k, v in self.tencent_smiley.items():
  81. if k in msg:
  82. msg = msg.replace(k, self.gen_replace_elem(v))
  83. return msg
  84. def replace_smileycode(self, msg):
  85. """ replace the smiley code in msg
  86. return a html
  87. """
  88. msg = self._replace_unicode(msg)
  89. msg = self._replace_tencent(msg)
  90. return msg
  91. def gen_used_smiley_css(self):
  92. ret = HEAD
  93. for sid in self.used_smiley_id:
  94. fname = os.path.join(STATIC_PATH, 'smileys', '{}.png'.format(sid))
  95. b64 = get_file_b64(fname)
  96. ret = ret + TEMPLATE.format(name=sid, b64=b64)
  97. return ret
  98. if __name__ == '__main__':
  99. smiley = SmileyProvider()
  100. msg = u"[挥手]哈哈呵呵hihi\U0001f684\u2728\u0001 /::<\ue415"
  101. msg = smiley.replace_smileycode(msg)
  102. #print msg
  103. smiley.gen_used_smiley_css()