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.

163 lines
5.3KB

  1. import re, os, sys, subprocess, copy, traceback, logging
  2. try:
  3. from HTMLParser import HTMLParser
  4. except ImportError:
  5. from html.parser import HTMLParser
  6. try:
  7. from urllib import quote as _quote
  8. quote = lambda n: _quote(n.encode('utf8', 'replace'))
  9. except ImportError:
  10. from urllib.parse import quote
  11. import requests
  12. from . import config
  13. logger = logging.getLogger('itchat')
  14. emojiRegex = re.compile(r'<span class="emoji emoji(.{1,10})"></span>')
  15. htmlParser = HTMLParser()
  16. if not hasattr(htmlParser, 'unescape'):
  17. import html
  18. htmlParser.unescape = html.unescape
  19. # FIX Python 3.9 HTMLParser.unescape is removed. See https://docs.python.org/3.9/whatsnew/3.9.html
  20. try:
  21. b = u'\u2588'
  22. sys.stdout.write(b + '\r')
  23. sys.stdout.flush()
  24. except UnicodeEncodeError:
  25. BLOCK = 'MM'
  26. else:
  27. BLOCK = b
  28. friendInfoTemplate = {}
  29. for k in ('UserName', 'City', 'DisplayName', 'PYQuanPin', 'RemarkPYInitial', 'Province',
  30. 'KeyWord', 'RemarkName', 'PYInitial', 'EncryChatRoomId', 'Alias', 'Signature',
  31. 'NickName', 'RemarkPYQuanPin', 'HeadImgUrl'):
  32. friendInfoTemplate[k] = ''
  33. for k in ('UniFriend', 'Sex', 'AppAccountFlag', 'VerifyFlag', 'ChatRoomId', 'HideInputBarFlag',
  34. 'AttrStatus', 'SnsFlag', 'MemberCount', 'OwnerUin', 'ContactFlag', 'Uin',
  35. 'StarFriend', 'Statues'):
  36. friendInfoTemplate[k] = 0
  37. friendInfoTemplate['MemberList'] = []
  38. def clear_screen():
  39. os.system('cls' if config.OS == 'Windows' else 'clear')
  40. def emoji_formatter(d, k):
  41. ''' _emoji_deebugger is for bugs about emoji match caused by wechat backstage
  42. like :face with tears of joy: will be replaced with :cat face with tears of joy:
  43. '''
  44. def _emoji_debugger(d, k):
  45. s = d[k].replace('<span class="emoji emoji1f450"></span',
  46. '<span class="emoji emoji1f450"></span>') # fix missing bug
  47. def __fix_miss_match(m):
  48. return '<span class="emoji emoji%s"></span>' % ({
  49. '1f63c': '1f601', '1f639': '1f602', '1f63a': '1f603',
  50. '1f4ab': '1f616', '1f64d': '1f614', '1f63b': '1f60d',
  51. '1f63d': '1f618', '1f64e': '1f621', '1f63f': '1f622',
  52. }.get(m.group(1), m.group(1)))
  53. return emojiRegex.sub(__fix_miss_match, s)
  54. def _emoji_formatter(m):
  55. s = m.group(1)
  56. if len(s) == 6:
  57. return ('\\U%s\\U%s'%(s[:2].rjust(8, '0'), s[2:].rjust(8, '0'))
  58. ).encode('utf8').decode('unicode-escape', 'replace')
  59. elif len(s) == 10:
  60. return ('\\U%s\\U%s'%(s[:5].rjust(8, '0'), s[5:].rjust(8, '0'))
  61. ).encode('utf8').decode('unicode-escape', 'replace')
  62. else:
  63. return ('\\U%s'%m.group(1).rjust(8, '0')
  64. ).encode('utf8').decode('unicode-escape', 'replace')
  65. d[k] = _emoji_debugger(d, k)
  66. d[k] = emojiRegex.sub(_emoji_formatter, d[k])
  67. def msg_formatter(d, k):
  68. emoji_formatter(d, k)
  69. d[k] = d[k].replace('<br/>', '\n')
  70. d[k] = htmlParser.unescape(d[k])
  71. def check_file(fileDir):
  72. try:
  73. with open(fileDir):
  74. pass
  75. return True
  76. except:
  77. return False
  78. def print_qr(fileDir):
  79. if config.OS == 'Darwin':
  80. subprocess.call(['open', fileDir])
  81. elif config.OS == 'Linux':
  82. subprocess.call(['xdg-open', fileDir])
  83. else:
  84. os.startfile(fileDir)
  85. def print_cmd_qr(qrText, white=BLOCK, black=' ', enableCmdQR=True):
  86. blockCount = int(enableCmdQR)
  87. if abs(blockCount) == 0:
  88. blockCount = 1
  89. white *= abs(blockCount)
  90. if blockCount < 0:
  91. white, black = black, white
  92. sys.stdout.write(' '*50 + '\r')
  93. sys.stdout.flush()
  94. qr = qrText.replace('0', white).replace('1', black)
  95. sys.stdout.write(qr)
  96. sys.stdout.flush()
  97. def struct_friend_info(knownInfo):
  98. member = copy.deepcopy(friendInfoTemplate)
  99. for k, v in copy.deepcopy(knownInfo).items(): member[k] = v
  100. return member
  101. def search_dict_list(l, key, value):
  102. ''' Search a list of dict
  103. * return dict with specific value & key '''
  104. for i in l:
  105. if i.get(key) == value:
  106. return i
  107. def print_line(msg, oneLine = False):
  108. if oneLine:
  109. sys.stdout.write(' '*40 + '\r')
  110. sys.stdout.flush()
  111. else:
  112. sys.stdout.write('\n')
  113. sys.stdout.write(msg.encode(sys.stdin.encoding or 'utf8', 'replace'
  114. ).decode(sys.stdin.encoding or 'utf8', 'replace'))
  115. sys.stdout.flush()
  116. def test_connect(retryTime=5):
  117. for i in range(retryTime):
  118. try:
  119. r = requests.get(config.BASE_URL)
  120. return True
  121. except:
  122. if i == retryTime - 1:
  123. logger.error(traceback.format_exc())
  124. return False
  125. def contact_deep_copy(core, contact):
  126. with core.storageClass.updateLock:
  127. return copy.deepcopy(contact)
  128. def get_image_postfix(data):
  129. data = data[:20]
  130. if b'GIF' in data:
  131. return 'gif'
  132. elif b'PNG' in data:
  133. return 'png'
  134. elif b'JFIF' in data:
  135. return 'jpg'
  136. return ''
  137. def update_info_dict(oldInfoDict, newInfoDict):
  138. ''' only normal values will be updated here
  139. because newInfoDict is normal dict, so it's not necessary to consider templates
  140. '''
  141. for k, v in newInfoDict.items():
  142. if any((isinstance(v, t) for t in (tuple, list, dict))):
  143. pass # these values will be updated somewhere else
  144. elif oldInfoDict.get(k) is None or v not in (None, '', '0', 0):
  145. oldInfoDict[k] = v