Skip to content

Commit af24a8e

Browse files
committed
Merge tag 'v1.5.0' into extend
2 parents 163c70a + 6043fd7 commit af24a8e

8 files changed

+95
-58
lines changed

README.md

+2-7
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,8 @@ _Codename_ **EH Forwarder Bot** (EFB) is an extensible chat tunnel framework whi
1919
## Media coverage
2020
* [Appinn: Send and Receive messages from WeChat on Telegram](http://www.appinn.com/eh-forwarder-bot/)
2121
_(EH Forwarder Bot – 在 Telegram 收发「微信」消息)_
22-
23-
## Glossary
24-
* **Channel**: A class that communicates with a chat platform, also known as a plugin.
25-
* **EFB**: abbreviation for EH Forwarder Bot, this project.
26-
* **Master channel**: A channel linked to the platform which directly interact with the user.
27-
* **Plugin**: See "channel".
28-
* **Slave channel**: A channel linked to the platform which is controlled by the user through EFB framework.
22+
* [Livc: Telegram — the true definition of IM](https://livc.io/177)
23+
_(Telegram——真正定义即时通讯)_
2924

3025
## Feel like contributing?
3126
Anyone is welcomed to raise an issue or submit a pull request, just remember to read through and understand the [contribution guideline](CONTRIBUTING.md) before you do so.

daemon.py

+20-8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import sys
1616
import os
17+
import inspect
18+
from binascii import crc32
1719
from datetime import datetime
1820
from functools import partial
1921
import time
@@ -269,7 +271,7 @@ def restart(self, name=None, group=None, cmd=None,
269271
f.write(pickle.dumps(dm))
270272
f.close()
271273
else:
272-
print('Daemon is not running.')
274+
raise NameError("Daemon is not running.")
273275

274276
def help():
275277
print("EFB Daemon Process\n"
@@ -299,25 +301,35 @@ def transcript(path, reset=False):
299301

300302
def main():
301303
transcript_path = "EFB.log"
304+
instance_name = str(crc32(os.path.dirname(os.path.abspath(inspect.stack()[0][1])).encode()))
302305
if len(sys.argv) < 2:
303306
help()
304307
exit()
305308
dm = DM()
306309
efb_args = " ".join(sys.argv[2:])
310+
if len(dm.get_daemons(name="EFB")):
311+
print("Old daemon process is killed.")
312+
dm.kill(name="EFB", quiet=True, sigkill=True)
307313
if sys.argv[1] == "start":
308314
dm.run(cmdline=" ".join((sys.executable + " main.py", efb_args)),
309-
name="EFB",
310-
logfile="EFB.log")
315+
name=instance_name,
316+
logfile=transcript_path)
311317
transcript(transcript_path, True)
312318
elif sys.argv[1] == "stop":
313-
dm.kill(name="EFB", quiet=True, sigkill=True)
319+
dm.kill(name=instance_name, quiet=True, sigkill=True)
314320
elif sys.argv[1] == "status":
315-
dm.list(name="EFB")
321+
dm.list(name=instance_name)
316322
elif sys.argv[1] == "restart":
317-
kwargs = {"name": "EFB", "quiet": True, "sigkill": True}
323+
kwargs = {"name": instance_name, "quiet": True, "sigkill": True}
318324
if len(sys.argv) > 2:
319-
kwargs["cmd"] = " ".join((sys.executable + " main.py", efb_args))
320-
dm.restart(**kwargs)
325+
kwargs["cmd"] = " ".join((sys.executable, "main.py", efb_args))
326+
try:
327+
dm.restart(**kwargs)
328+
except NameError as e:
329+
print(e)
330+
dm.run(cmdline=" ".join((sys.executable + " main.py", efb_args)),
331+
name=instance_name,
332+
logfile=transcript_path)
321333
transcript(transcript_path, True)
322334
elif sys.argv[1] == "transcript":
323335
transcript(transcript_path)

docs/ETM.md

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ At the beginning, messages from all senders will be sent to the user directly, t
7777
In a nutshell, ETM offers the following commands, you can also send it to BotFather for the command list.
7878

7979
```
80+
help - Show commands list.
8081
link - Link a remote chat to a group.
8182
unlink_all - Unlink all remote chats from a group.
8283
chat - Generate a chat head.

docs/index.md

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ _Codename_ **EH Forwarder Bot** (EFB) is an extensible chat tunnel framework whi
3030
## Media coverage
3131
* [Appinn: Send and Receive messages from WeChat on Telegram](http://www.appinn.com/eh-forwarder-bot/)
3232
_(EH Forwarder Bot – 在 Telegram 收发「微信」消息)_
33+
* [Livc: Telegram — the true definition of IM](https://livc.io/177)
34+
_(Telegram——真正定义即时通讯)_
3335

3436
## Glossary
3537
* **Channel**: A class that communicates with a chat platform, also known as a plugin.

docs/installation.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ EFB framework itself does not require any external modules to operate. However,
33

44
To install dependencies for **all** officially maintained channels, you may follow the following instructions, or refer to the respective documentations of each channel.
55

6+
Firstly, clone this project and open it.
7+
8+
```bash
9+
git clone https://github.com/blueset/ehForwarderBot.git
10+
cd ehForwarderBot
11+
```
12+
613
## Storage directory
714

815
In order to process files and media (pictures, voices, videos, etc.), a storage folder is used to temporarily save and process them. Create a `storage` folder, if not existing, and give write and read permission to it.
@@ -30,7 +37,7 @@ For more information regarding installation of Pillow, please visit [Pillow docu
3037
Install [Homebrew](https://brew.sh), then:
3138

3239
```bash
33-
brew install libtiff libjpeg webp little-cms2
40+
brew install libjpeg webp python3
3441
brew install libmagic
3542
brew install ffmpeg --with-opus
3643
```
@@ -39,7 +46,7 @@ brew install ffmpeg --with-opus
3946

4047
```bash
4148
sudo apt-get install python3-dev python3-setuptools
42-
sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev
49+
sudo apt-get install libwebp-dev
4350
sudo apt-get install libmagic-dev ffmpeg
4451
```
4552

@@ -49,7 +56,7 @@ Refer to `requirements.txt`, or [Channels Repository](channels-repository.md) fo
4956

5057
### To install
5158
```bash
52-
pip(3) install -r requirements.txt
59+
pip3 install -r requirements.txt
5360
```
5461

5562
> If you'd like to start to give EFB a try, you can now head to the [Getting started](getting-started.md) page.

main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
if sys.version_info.major < 3:
1111
raise Exception("Python 3.x is required. Your version is %s." % sys.version)
1212

13-
__version__ = "1.4.4-dev.12"
13+
__version__ = "1.5.0"
1414

1515
parser = argparse.ArgumentParser(description="EH Forwarder Bot is an extensible chat tunnel framework which allows "
1616
"users to contact people from other chat platforms, and ultimately "

plugins/eh_wechat_slave.py

+56-37
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import itchat
1212
import magic
1313
import xmltodict
14+
from pyqrcode import QRCode
1415
from PIL import Image
1516

1617
import config
@@ -41,7 +42,8 @@ def wrap_func(self, msg, *args, **kwargs):
4142
logger.debug("Group chat")
4243
if me:
4344
msg['ActualUserName'] = msg['ToUserName']
44-
member = {"NickName": self.itchat.loginInfo['User']['NickName'], "DisplayName": "You",
45+
member = {"NickName": self._wechat_html_unescape(self.itchat.loginInfo['User']['NickName']),
46+
"DisplayName": "You",
4547
"Uin": self.itchat.loginInfo['User']['Uin']}
4648
else:
4749
logger.debug("search_user")
@@ -53,16 +55,16 @@ def wrap_func(self, msg, *args, **kwargs):
5355
'name': html.unescape(FromUser['NickName']),
5456
'alias': html.unescape(FromUser['RemarkName'] or FromUser['NickName']),
5557
'uid': self.get_uid(UserName=msg.get('FromUserName', None),
56-
NickName=html.unescape(FromUser.get('NickName', "s")),
57-
alias=html.unescape(FromUser.get('RemarkName', "s")),
58+
NickName=self._wechat_html_unescape(FromUser.get('NickName', "s")),
59+
alias=self._wechat_html_unescape(FromUser.get('RemarkName', "s")),
5860
Uin=FromUser.get('Uin', None))
5961
}
6062
mobj.member = {
61-
'name': html.unescape(member['NickName']),
62-
'alias': html.unescape(member['DisplayName']),
63+
'name': self._wechat_html_unescape(member['NickName']),
64+
'alias': self._wechat_html_unescape(member['DisplayName']),
6365
'uid': self.get_uid(UserName=msg.get('ActualUserName', None),
64-
NickName=html.unescape(member.get('NickName', "")),
65-
alias=html.unescape(member.get('DisplayName', "")),
66+
NickName=self._wechat_html_unescape(member.get('NickName', "")),
67+
alias=self._wechat_html_unescape(member.get('DisplayName', "")),
6668
Uin=member.get('Uin', None))
6769
}
6870
logger.debug("origin: %s\nmember: %s\n", mobj.origin, mobj.member)
@@ -72,16 +74,16 @@ def wrap_func(self, msg, *args, **kwargs):
7274
mobj.text = "You: " + mobj.text
7375
mobj.source = MsgSource.User
7476
mobj.origin = {
75-
'name': html.unescape(FromUser['NickName']),
76-
'alias': html.unescape(FromUser['RemarkName'] or FromUser['NickName']),
77+
'name': self._wechat_html_unescape(FromUser['NickName']),
78+
'alias': self._wechat_html_unescape(FromUser['RemarkName'] or FromUser['NickName']),
7779
'uid': self.get_uid(UserName=msg.get('FromUserName', None),
78-
NickName=html.unescape(FromUser.get('NickName', "")),
79-
alias=html.unescape(FromUser.get('RemarkName', "")),
80+
NickName=self._wechat_html_unescape(FromUser.get('NickName', "")),
81+
alias=self._wechat_html_unescape(FromUser.get('RemarkName', "")),
8082
Uin=FromUser.get('Uin', None))
8183
}
8284
mobj.destination = {
83-
'name': self.itchat.loginInfo['User']['NickName'],
84-
'alias': self.itchat.loginInfo['User']['NickName'],
85+
'name': self._wechat_html_unescape(self.itchat.loginInfo['User']['NickName']),
86+
'alias': self._wechat_html_unescape(self.itchat.loginInfo['User']['NickName']),
8587
'uid': self.get_uid(UserName=msg['ToUserName'])
8688
}
8789
logger.debug("dest: %s", mobj.destination)
@@ -144,16 +146,8 @@ def console_qr_code(self, uuid, status, qrcode):
144146
if status == 408:
145147
QR += "Previous code expired. Please scan the new one.\n"
146148
QR += "\n"
147-
time.sleep(0.5)
148-
img = Image.open(io.BytesIO(qrcode)).convert("1")
149-
img = img.resize((img.size[0] // 10, img.size[1] // 10))
150-
for i in range(img.size[0]):
151-
for j in range(img.size[1]):
152-
if img.getpixel((i, j)) > 0:
153-
QR += "\x1b[7m \x1b[0m"
154-
else:
155-
QR += "\x1b[49m \x1b[0m"
156-
QR += "\n"
149+
qr_url = "https://login.weixin.qq.com/l/" + uuid
150+
QR += QRCode(qr_url).terminal()
157151
QR += "\nIf you cannot read the QR code above, " \
158152
"please visit the following URL:\n" \
159153
"https://login.weixin.qq.com/qrcode/" + uuid
@@ -183,8 +177,8 @@ def master_qr_code(self, uuid, status, qrcode):
183177
os.makedirs(path)
184178
path = os.path.join(path, 'QR-%s.jpg' % int(time.time()))
185179
self.logger.debug("master_qr_code file path: %s", path)
186-
with open(path, 'wb') as f:
187-
f.write(qrcode)
180+
qr_url = "https://login.weixin.qq.com/l/" + uuid
181+
QRCode(qr_url).png(path, scale=10)
188182
msg.text = 'Scan this QR Code with WeChat to continue.'
189183
msg.path = path
190184
msg.file = open(path, 'rb')
@@ -329,34 +323,40 @@ def search_user(self, UserName=None, uid=None, uin=None, name=None, ActualUserNa
329323
"Uin": sys_chat_id}]
330324

331325
for i in self.itchat.get_friends(refresh) + self.itchat.get_mps(refresh):
332-
data = {"nickname": i.get('NickName', None),
333-
"alias": i.get("RemarkName", None),
326+
data = {"nickname": self._wechat_html_unescape(i.get('NickName', None)),
327+
"alias": self._wechat_html_unescape(i.get("RemarkName", None)),
334328
"uin": i.get("Uin", None)}
335329
if self.encode_uid(data) == uid or \
336330
str(i.get('UserName', '')) == UserName or \
337331
str(i.get('AttrStatus', '')) == uid or \
338332
str(i.get('Uin', '')) == uin or \
339333
str(i.get('NickName', '')) == name or \
340334
str(i.get('DisplayName', '')) == name:
335+
i['NickName'] = self._wechat_html_unescape(i.get('NickName', ''))
336+
i['DisplayName'] = self._wechat_html_unescape(i.get('DisplayName', ''))
341337
result.append(i.copy())
342338
for i in self.itchat.get_chatrooms(refresh):
343339
if not i['UserName'].startswith('@@'):
344340
continue
345341
if not i.get('MemberList', ''):
346342
i = self.itchat.update_chatroom(i.get('UserName', ''))
347-
data = {"nickname": i.get('NickName', None),
348-
"alias": i.get("RemarkName", None),
343+
data = {"nickname": self._wechat_html_unescape(i.get('NickName', None)),
344+
"alias": self._wechat_html_unescape(i.get("RemarkName", None)),
349345
"uin": i.get("Uin", None)}
350346
if self.encode_uid(data) == uid or \
351347
str(i.get('Uin', '')) == uin or \
352348
str(i.get('NickName', '')) == name or \
353349
str(i.get('DisplayName', '')) == name or \
354350
str(i.get('UserName', '')) == UserName:
351+
i['NickName'] = self._wechat_html_unescape(i.get('NickName', ''))
352+
i['DisplayName'] = self._wechat_html_unescape(i.get('DisplayName', ''))
355353
result.append(i.copy())
356354
result[-1]['MemberList'] = []
357355
if ActualUserName:
358356
for j in i['MemberList']:
359357
if str(j['UserName']) == ActualUserName:
358+
j['NickName'] = self._wechat_html_unescape(j.get('NickName', ''))
359+
j['DisplayName'] = self._wechat_html_unescape(j.get('DisplayName', ''))
360360
result[-1]['MemberList'].append(j)
361361
if not result and not refresh:
362362
return self.search_user(UserName, uid, uin, name, ActualUserName, refresh=True)
@@ -726,8 +726,8 @@ def get_chat_list(self, param=""):
726726

727727
msg = "List of chats:\n"
728728
for n, i in enumerate(l):
729-
alias = i.get('RemarkName', '') or i.get('DisplayName', '')
730-
name = i.get('NickName', '')
729+
alias = self._wechat_html_unescape(i.get('RemarkName', '') or i.get('DisplayName', ''))
730+
name = self._wechat_html_unescape(i.get('NickName', ''))
731731
x = "%s (%s)" % (alias, name) if alias else name
732732
msg += "\n%s: [%s] %s" % (n, x, i['Type'])
733733

@@ -857,9 +857,11 @@ def get_chats(self, group=True, user=True):
857857
r.append({
858858
'channel_name': self.channel_name,
859859
'channel_id': self.channel_id,
860-
'name': i['NickName'],
861-
'alias': i['RemarkName'] or i['NickName'],
862-
'uid': self.get_uid(UserName=i['UserName'], NickName=i['NickName'], alias=i.get("RemarkName", None),
860+
'name': self._wechat_html_unescape(i['NickName']),
861+
'alias': self._wechat_html_unescape(i['RemarkName'] or i['NickName']),
862+
'uid': self.get_uid(UserName=i['UserName'],
863+
NickName=self._wechat_html_unescape(i['NickName']),
864+
alias=self._wechat_html_unescape(i.get("RemarkName", None)),
863865
Uin=i.get("Uin", None)),
864866
'type': MsgSource.User
865867
})
@@ -871,9 +873,11 @@ def get_chats(self, group=True, user=True):
871873
r.append({
872874
'channel_name': self.channel_name,
873875
'channel_id': self.channel_id,
874-
'name': i['NickName'],
875-
'alias': i['RemarkName'] or i['NickName'] or None,
876-
'uid': self.get_uid(NickName=i['NickName'], alias=i.get("RemarkName", None),
876+
'name': self._wechat_html_unescape(i['NickName']),
877+
'alias': self._wechat_html_unescape(i['RemarkName'] or i['NickName']),
878+
'uid': self.get_uid(UserName=i['UserName'],
879+
NickName=self._wechat_html_unescape(i['NickName']),
880+
alias=self._wechat_html_unescape(i.get("RemarkName", None)),
877881
Uin=i.get("Uin", None)),
878882
'type': MsgSource.Group
879883
})
@@ -968,3 +972,18 @@ def _itchat_send_video(self, *args, **kwargs):
968972
return self.itchat.send_video(*args, **kwargs)
969973
except Exception as e:
970974
raise EFBMessageError(repr(e))
975+
976+
@staticmethod
977+
def _wechat_html_unescape(content):
978+
"""
979+
Unescape a WeChat HTML string.
980+
981+
Args:
982+
content (str): String to be formatted
983+
984+
Returns:
985+
str: Unescaped string.
986+
"""
987+
d = {"Content": content}
988+
itchat.utils.msg_formatter(d, "Content")
989+
return d['Content']

requirements.txt

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ python_telegram_bot>=5.3
66
xmltodict
77
Pillow
88
python_magic
9-
itchat>=1.2.18
10-
python-daemon
9+
itchat>=1.2.24
10+
python-daemon
11+
pyqrcode

0 commit comments

Comments
 (0)