├── .gitignore
├── BaiduFM.py
├── ControMqtt.py
├── Dictionary.py
├── Direction.py
├── EmailMyPC.py
├── Halt.py
├── HeadlineNews.py
├── Lamp.py
├── Lock.py
├── Locker
├── NetEaseMusic.py
├── README.md
├── RaspberryPiStatus.py
├── Reboot.py
├── RoadCondition.py
├── SendMessage.py
├── SpeakIP.py
├── SpeakTemperature.py
├── ToDo.py
├── WOL.py
├── Weather.py
├── WebServer.py
├── WeiboHot.py
├── YouDaoFanYi.py
├── requirements.txt
└── xiaoice.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.pyc
3 | #*#
4 | .#*
5 |
--------------------------------------------------------------------------------
/BaiduFM.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | import json
3 | import logging
4 | import os
5 | import socket
6 | import subprocess
7 | import sys
8 | import tempfile
9 | import threading
10 | from urllib import urlopen
11 |
12 | reload(sys)
13 | sys.setdefaultencoding('utf8')
14 | socket.setdefaulttimeout(10)
15 |
16 | WORDS = ["BAIDUYINYUE"]
17 | SLUG = "baidufm"
18 |
19 | DEFAULT_CHANNEL = 14
20 |
21 |
22 | class MusicPlayer(threading.Thread):
23 |
24 | def __init__(self, playlist, logger):
25 | super(MusicPlayer, self).__init__()
26 | self.event = threading.Event()
27 | self.event.set()
28 | self.playlist = playlist
29 | self.idx = 0
30 | self.is_stop = False
31 | self.is_pause = False
32 | self.song_file = "dummy"
33 | self.directory = tempfile.mkdtemp()
34 | self.logger = logger
35 |
36 | def run(self):
37 | while True:
38 | if self.event.wait():
39 | self.play()
40 | if not self.is_pause:
41 | self.pick_next()
42 |
43 | def play(self):
44 | self.logger.debug('MusicPlayer play')
45 | song_url = "http://music.baidu.com/data/music/fmlink?" +\
46 | "type=mp3&rate=320&songIds=%s" % self.playlist[self.idx]['id']
47 | song_name, song_link, song_size, song_time =\
48 | self.get_song_real_url(song_url)
49 | self.download_mp3_by_link(song_link, song_name, song_size)
50 | self.play_mp3_by_link(song_link, song_name, song_size, song_time)
51 |
52 | def get_song_real_url(self, song_url):
53 | try:
54 | htmldoc = urlopen(song_url).read().decode('utf8')
55 | except:
56 | return(None, None, 0, 0)
57 |
58 | content = json.loads(htmldoc)
59 |
60 | try:
61 | song_link = content['data']['songList'][0]['songLink']
62 | song_name = content['data']['songList'][0]['songName']
63 | song_size = int(content['data']['songList'][0]['size'])
64 | song_time = int(content['data']['songList'][0]['time'])
65 | except:
66 | self.logger.error('get real link failed')
67 | return(None, None, 0, 0)
68 |
69 | return song_name, song_link, song_size, song_time
70 |
71 | def play_mp3_by_link(self, song_link, song_name, song_size, song_time):
72 | process = subprocess.Popen("pkill play", shell=True)
73 | process.wait()
74 | if os.path.exists(self.song_file):
75 | if not self.is_stop:
76 | cmd = ['play', self.song_file]
77 | self.logger.debug('begin to play')
78 | with tempfile.TemporaryFile() as f:
79 | subprocess.call(cmd, stdout=f, stderr=f)
80 | f.seek(0)
81 | output = f.read()
82 | print(output)
83 | self.logger.debug('play done')
84 | if not self.is_pause:
85 | self.logger.debug('song_file remove')
86 | os.remove(self.song_file)
87 |
88 | def download_mp3_by_link(self, song_link, song_name, song_size):
89 | file_name = song_name + ".mp3"
90 |
91 | self.song_file = os.path.join(self.directory, file_name)
92 | if os.path.exists(self.song_file):
93 | return
94 | self.logger.debug("begin DownLoad %s size %d" % (song_name, song_size))
95 | mp3 = urlopen(song_link)
96 |
97 | block_size = 8192
98 | down_loaded_size = 0
99 |
100 | file = open(self.song_file, "wb")
101 | while True and not self.is_stop:
102 | try:
103 | buffer = mp3.read(block_size)
104 |
105 | down_loaded_size += len(buffer)
106 |
107 | if(len(buffer) == 0):
108 | if down_loaded_size < song_size:
109 | if os.path.exists(self.song_file):
110 | os.remove(self.song_file)
111 | break
112 | file.write(buffer)
113 |
114 | if down_loaded_size >= song_size:
115 | self.logger.debug('%s download finshed' % self.song_file)
116 | break
117 |
118 | except:
119 | if os.path.getsize(self.song_file) < song_size:
120 | self.logger.debug('song_file remove')
121 | if os.path.exists(self.song_file):
122 | os.remove(self.song_file)
123 | break
124 |
125 | file.close()
126 |
127 | def pick_next(self):
128 | self.idx += 1
129 | if self.idx > len(self.playlist) - 1:
130 | self.idx = 0
131 |
132 | def pause(self):
133 | try:
134 | self.event.clear()
135 | self.is_pause = True
136 | subprocess.Popen("pkill play", shell=True)
137 | except:
138 | pass
139 |
140 | def resume(self):
141 | self.is_pause = False
142 | self.event.set()
143 |
144 | def stop(self):
145 | self.pause()
146 | self.is_stop = True
147 | self.playlist = []
148 | if os.path.exists(self.song_file):
149 | os.remove(self.song_file)
150 | if os.path.exists(self.directory):
151 | os.removedirs(self.directory)
152 |
153 |
154 | def get_channel_list(page_url):
155 | try:
156 | htmldoc = urlopen(page_url).read().decode('utf8')
157 | except:
158 | return {}
159 |
160 | content = json.loads(htmldoc)
161 | channel_list = content['channel_list']
162 |
163 | return channel_list
164 |
165 |
166 | def get_song_list(channel_url):
167 | try:
168 | htmldoc = urlopen(channel_url).read().decode('utf8')
169 | except:
170 | return{}
171 |
172 | content = json.loads(htmldoc)
173 | song_id_list = content['list']
174 |
175 | return song_id_list
176 |
177 |
178 | def handle(text, mic, profile, bot=None):
179 | logger = logging.getLogger(__name__)
180 | page_url = 'http://fm.baidu.com/dev/api/?tn=channellist'
181 | channel_list = get_channel_list(page_url)
182 |
183 | if 'robot_name' in profile:
184 | persona = profile['robot_name']
185 |
186 | channel = DEFAULT_CHANNEL
187 |
188 | if SLUG in profile and 'channel' in profile[SLUG]:
189 | channel = profile[SLUG]['channel']
190 |
191 | channel_id = channel_list[channel]['channel_id']
192 | channel_name = channel_list[channel]['channel_name']
193 | mic.say(u"播放" + channel_name)
194 |
195 | while True:
196 | channel_url = 'http://fm.baidu.com/dev/api/' +\
197 | '?tn=playlist&format=json&id=%s' % channel_id
198 | song_id_list = get_song_list(channel_url)
199 |
200 | music_player = MusicPlayer(song_id_list, logger)
201 | music_player.start()
202 |
203 | while True:
204 | try:
205 | threshold, transcribed = mic.passiveListen(persona)
206 | except Exception, e:
207 | logger.error(e)
208 | threshold, transcribed = (None, None)
209 |
210 | if not transcribed or not threshold:
211 | logger.info("Nothing has been said or transcribed.")
212 | continue
213 |
214 | music_player.pause()
215 | input = mic.activeListen()
216 |
217 | if input and any(ext in input for ext in [u"结束", u"退出", u"停止"]):
218 | mic.say(u"结束播放", cache=True)
219 | music_player.stop()
220 | return
221 | else:
222 | mic.say(u"什么?", cache=True)
223 | music_player.resume()
224 |
225 |
226 | def isValid(text):
227 | return any(word in text for word in [u"百度音乐", u"百度电台"])
228 |
--------------------------------------------------------------------------------
/ControMqtt.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8
2 | #author: chenzhuo
3 | #Raspberry Pi or other platform can connect to the mqtt client,publisher and subscriber can access to bidirectional communication by switching their identities.
4 | #Example:you can get temperature of the enviroment collected by Arduino using Raspberry Pi when Raspberry Pi and Arduino communicate with each other.
5 | #The actions' file must be /home/pi/.dingdang/action.json
6 |
7 | import paho.mqtt.client as mqtt
8 | import paho.mqtt.publish as publish
9 | import logging
10 | import time
11 | import json
12 | import os
13 | import sys
14 | reload(sys)
15 | sys.setdefaultencoding('utf8')
16 |
17 | WORDS = ["BUGUANG","JIAOSHUI"]
18 | SLUG = "mqttPub"
19 |
20 | def get_topic(text):
21 | home_dir = os.path.expandvars('$HOME')
22 | location = home_dir + '/.dingdang/action.json'
23 | f = open(location).read()
24 | fjson = json.loads(f)
25 | topic = None
26 | for key in fjson.keys():
27 | if text in fjson[key]:
28 | topic = key
29 | return topic
30 |
31 | def handle(text,mic,profile,wxbot=None):
32 | logger = logging.getLogger(__name__)
33 |
34 | #get config
35 | if ( SLUG not in profile ) or ( not profile[SLUG].has_key('host') ) or ( not profile[SLUG].has_key('port') ) or ( not profile[SLUG].has_key('topic_s') ):
36 | mic.say("主人,配置有误", cache=True)
37 | return
38 |
39 | host = profile[SLUG]['host']
40 | port = profile[SLUG]['port']
41 | topic_s = profile[SLUG]['topic_s']
42 | #print topic_s
43 | text = text.split(",")[0] #百度语音识别返回的数据中有个中文,
44 | topic_p = get_topic(text)
45 | #print "topic_p is " + topic_p
46 | if topic_p == None:
47 | return
48 | try:
49 | mic.say("已经接收到指令", cache=True)
50 | mqtt_contro(host,port,topic_s,topic_p,text,mic)
51 | except Exception, e:
52 | logger.error(e)
53 | mic.say("抱歉出了问题", cache=True)
54 | return
55 |
56 | def isValid(text):
57 | home_dir = os.path.expandvars('$HOME')
58 | location = home_dir + '/.dingdang/action.json'
59 | words = []
60 | if os.path.exists(location):
61 | f = open(location).read()
62 | try:
63 | fjson = json.loads(f)
64 | for value in fjson.values():
65 | if isinstance(value,list):
66 | words += value
67 | else:
68 | words += []
69 | except ValueError:
70 | words += []
71 | #lines = f.readlines()
72 | #if len(lines):
73 | # for line in lines:
74 | # line = line.split()
75 | # if len(line):
76 | # words.append(line[0])
77 | return any(word in text for word in words)
78 |
79 | class mqtt_contro(object):
80 |
81 | def __init__(self,host,port,topic_s,topic_p,message,mic):
82 | self._logger = logging.getLogger(__name__)
83 | self.host = host
84 | self.port = port
85 | self.topic_s = topic_s
86 | self.topic_p = topic_p
87 | self.message = message
88 | self.mic = mic
89 | self.mqttc = mqtt.Client()
90 | self.mqttc.on_message = self.on_message
91 | self.mqttc.on_connect = self.on_connect
92 | #mqttc.on_publish = on_publish
93 | #mqttc.on_subscribe = on_subscribe
94 | #mqttc.on_log = on_log
95 | if self.host and self.topic_p:
96 | publish.single(self.topic_p, payload=self.message, hostname=self.host,port=1883)
97 | if self.port and self.topic_s and self.host:
98 | self.mqttc.connect(self.host, self.port, 5)
99 | self.mqttc.subscribe(topic_s, 0)
100 | #while True:
101 | # self.mqttc.loop(timeout=5)
102 | self.mqttc.loop_start()
103 |
104 | def on_connect(self,mqttc, obj, flags, rc):
105 | if rc == 0:
106 | pass
107 | else:
108 | print("error connect")
109 |
110 | def on_message(self,mqttc, obj, msg):
111 | #print(str(msg.payload))
112 | if msg.payload:
113 | self.mqttc.loop_stop()
114 | self.mqttc.disconnect()
115 | self.mic.say( str(msg.payload) )
116 | else:
117 | time.sleep(5)
118 | self.mqttc.loop_stop()
119 | self.mqttc.disconnect()
120 | self.mic.say("连接超时", cache=True)
121 |
122 | def on_publish(self,mqttc, obj, mid):
123 | print("mid: " + str(mid))
124 |
125 | def on_subscribe(self,mqttc, obj, mid, granted_qos):
126 | print("Subscribed: " + str(mid) + " " + str(granted_qos))
127 |
128 | def on_log(self,mqttc, obj, level, string):
129 | print(string)
130 |
--------------------------------------------------------------------------------
/Dictionary.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import urllib
3 | import re
4 | from urllib import urlencode
5 | import sys
6 |
7 | reload(sys)
8 | sys.setdefaultencoding('utf8')
9 |
10 | SLUG = "dictionary"
11 | WORDS = ["CHENGYU"]
12 |
13 |
14 | def getHtml(words):
15 | url = 'http://dict.baidu.com/s'
16 | values = {'wd' : words}
17 | data = urlencode(values)
18 | html = ""
19 | try:
20 | response = urllib.urlopen("%s?%s" % (url, data))
21 | html = response.read()
22 | except :
23 | pass
24 | return html
25 |
26 | def handleHtml(html):
27 | patten1 = re.compile('
.*?
', re.S)
28 | results = re.findall(patten1, html)
29 | str = ""
30 | for i in results:
31 | if "出自" in i:
32 | patten2 = re.compile("(.*?)", re.S)
33 | results2 = re.findall(patten2, i)
34 | str = results2[0] + results2[1]
35 | return str
36 |
37 | def getWords(text):
38 | pattern1 = re.compile("成语.*?")
39 | pattern2 = re.compile(".*?的成语意思")
40 |
41 | if re.match(pattern1, text) != None:
42 | words = text.replace("成语", "")
43 | elif re.match(pattern2, text) != None:
44 | words = text.replace("的成语意思", "")
45 | else:
46 | words = ""
47 | words = words.replace(",","")
48 | words = words.replace(",","")
49 | return words
50 |
51 | def info(html):
52 | pass
53 |
54 | def handle(text, mic, profile, wxbot=None):
55 | words = getWords(text)
56 | if words:
57 | html = getHtml(words)
58 | info(html)
59 | if html:
60 | str = handleHtml(html)
61 | if str:
62 | mic.say(words + str, cache=True)
63 | else:
64 | mic.say("成语" + words +"有误 请重试", cache=True)
65 |
66 | else:
67 | mic.say(u"网络连接有误 请重试", cache=True)
68 | else:
69 | mic.say(u"没有听清楚 请重试", cache=True)
70 |
71 | def isValid(text):
72 | return u"成语" in text
73 |
--------------------------------------------------------------------------------
/Direction.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | import sys
3 | import os
4 | import logging
5 | import json, urllib
6 | from urllib import urlencode
7 |
8 | reload(sys)
9 | sys.setdefaultencoding('utf8')
10 |
11 | WORDS = ["XIANLU"]
12 | SLUG = "direction"
13 |
14 | def request(url, params):
15 | params = urlencode(params)
16 |
17 | f = urllib.urlopen("%s?%s" % (url, params))
18 |
19 | content = f.read()
20 | return json.loads(content)
21 |
22 | def handle(text, mic, profile, wxbot=None):
23 | logger = logging.getLogger(__name__)
24 |
25 | mic.say(u'去哪里')
26 | input = mic.activeListen(MUSIC=True)
27 | if input is None:
28 | mic.say(u'已取消')
29 | return
30 |
31 | if SLUG not in profile or \
32 | 'app_key' not in profile[SLUG] or \
33 | 'city' not in profile[SLUG] or \
34 | 'origin' not in profile[SLUG] or \
35 | 'method' not in profile[SLUG]:
36 | mic.say(u"插件配置有误,插件使用失败")
37 | return
38 |
39 | app_key = profile[SLUG]['app_key']
40 | city = profile[SLUG]['city']
41 |
42 | url_place = "http://api.map.baidu.com/place/v2/suggestion"
43 | params_place = {
44 | "query" : input,
45 | "region" : city,
46 | "city_limit" : "true",
47 | "output" : "json",
48 | "ak" : app_key,
49 | }
50 |
51 | res = request(url_place, params_place)
52 |
53 | if res:
54 | status = res["status"]
55 | if status == 0:
56 | if len(res['result']) > 0:
57 | place_name = res['result'][0]["name"]
58 | destination = "%f,%f" % (res['result'][0]["location"]['lat'], res['result'][0]["location"]['lng'])
59 | else:
60 | mic.say(u"错误的位置")
61 | return
62 | else:
63 | logger.error(u"位置接口:" + res['message'])
64 | return
65 | else:
66 | logger.error(u"位置接口调用失败")
67 | return
68 |
69 | origin = profile[SLUG]['origin']
70 |
71 | url_direction = "http://api.map.baidu.com/direction/v2/transit"
72 | params_direction = {
73 | "origin" : origin,
74 | "destination" : destination,
75 | "page_size" : 1,
76 | "ak" : app_key,
77 | }
78 |
79 | res = request(url_direction, params_direction)
80 | if res:
81 | status = res["status"]
82 | if status == 0:
83 | if len(res['result']['routes']) > 0:
84 | direction = ""
85 | for step in res['result']['routes'][0]['steps']:
86 | direction = direction + step[0]["instructions"] + "."
87 | result = place_name + u"参考路线:" + direction
88 | if 'method' in profile[SLUG]:
89 | if profile[SLUG]['method'] == "voice" or \
90 | wxbot is None:
91 | mic.say(result)
92 | else:
93 | wxbot.send_msg_by_uid(result, 'filehelper')
94 | mic.say(u"已发送到微信")
95 | else:
96 | mic.say(u"导航错误")
97 | return
98 | else:
99 | logger.error(u"导航接口:" + res['message'])
100 | return
101 | else:
102 | logger.error(u"导航接口调用失败")
103 | return
104 |
105 | def isValid(text):
106 | return any(word in text for word in [u"怎么去", u"线路", u"路线"])
107 |
--------------------------------------------------------------------------------
/EmailMyPC.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | import sys
3 | import logging
4 | from app_utils import sendEmail
5 |
6 | reload(sys)
7 | sys.setdefaultencoding('utf8')
8 |
9 | WORDS = ["DIANNAO"]
10 | SLUG = "emailmypc"
11 |
12 | def handle(text, mic, profile, wxbot=None):
13 | logger = logging.getLogger(__name__)
14 | if 'email' not in profile or ('enable' in profile['email']
15 | and not profile['email']):
16 | mic.say(u'请先配置好邮箱功能', cache=True)
17 | return
18 | address = profile['email']['address']
19 | password = profile['email']['password']
20 | smtp_server = profile['email']['smtp_server']
21 | smtp_port = profile['email']['smtp_port']
22 | if SLUG not in profile or \
23 | not profile[SLUG].has_key('pc_email'):
24 | mic.say('远控插件配置有误,插件使用失败', cache=True)
25 | return
26 | pc_email = profile[SLUG]['pc_email']
27 | try:
28 | if any(word in text for word in [u"关机", u"关电脑", u"关闭电脑"]):
29 | mic.say('即将关闭电脑,请在滴一声后进行确认', cache=True)
30 | input = mic.activeListen(MUSIC=True)
31 | if input is not None and any(word in input for word in [u"确认", u"好", u"是", u"OK"]):
32 | sendEmail("#shutdown", "", "", pc_email, address, address, password, smtp_server, smtp_port)
33 | mic.say('已发送关机指令', cache=True)
34 | else:
35 | mic.say('已取消', cache=True)
36 | if any(word in text for word in [u"屏幕", u"截图"]):
37 | sendEmail("#screen", "", "", pc_email, address, address, password, smtp_server, smtp_port)
38 | mic.say('已发送截图指令,请查看您的邮箱', cache=True)
39 | if any(word in text for word in [u"摄像头"]):
40 | sendEmail("#cam", "", "", pc_email, address, address, password, smtp_server, smtp_port)
41 | mic.say('已发送拍照指令,请查看您的邮箱', cache=True)
42 | if any(word in text for word in [u"快捷键"]):
43 | if SLUG not in profile or \
44 | not profile[SLUG].has_key('button'):
45 | mic.say('您还未设置快捷键', cache=True)
46 | return
47 | button = profile[SLUG]['button']
48 | mic.say('即将发送快捷键%s,请在滴一声后进行确认' % button, cache=True)
49 | input = mic.activeListen(MUSIC=True)
50 | if input is not None and any(word in input for word in [u"确认", u"好", u"是", u"OK"]):
51 | sendEmail("#button", button, "", pc_email, address, address, password, smtp_server, smtp_port)
52 | mic.say('已发送快捷键', cache=True)
53 | else:
54 | mic.say('已取消', cache=True)
55 | if any(word in text for word in [u"命令", u"指令"]):
56 | if SLUG not in profile or \
57 | not profile[SLUG].has_key('cmd'):
58 | mic.say('您还未设置指令', cache=True)
59 | return
60 | cmd = profile[SLUG]['cmd']
61 | mic.say('即将发送指令%s,请在滴一声后进行确认' % cmd, cache=True)
62 | input = mic.activeListen(MUSIC=True)
63 | if input is not None and any(word in input for word in [u"确认", u"好", u"是", u"OK"]):
64 | sendEmail("#cmd", cmd, "", pc_email, address, address, password, smtp_server, smtp_port)
65 | mic.say('已发送指令', cache=True)
66 | else:
67 | mic.say('已取消', cache=True)
68 | except Exception, e:
69 | logger.error(e)
70 | mic.say('抱歉,指令发送失败', cache=True)
71 |
72 | def isValid(text):
73 | return any(word in text for word in [u"关电脑", u"关闭电脑", u"屏幕", u"截图", u"摄像头", u"快捷键", u"命令", u"指令"])
74 |
--------------------------------------------------------------------------------
/Halt.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 关闭系统插件
3 | import logging
4 | import sys
5 | import time
6 | import subprocess
7 |
8 | reload(sys)
9 | sys.setdefaultencoding('utf8')
10 |
11 | WORDS = ["GUANJI"]
12 | SLUG = "halt"
13 |
14 | def handle(text, mic, profile, wxbot=None):
15 | logger = logging.getLogger(__name__)
16 | try:
17 | mic.say('将要关闭系统,请在滴一声后进行确认,授权相关操作', cache=True)
18 | input = mic.activeListen(MUSIC=True)
19 | if input is not None and any(word in input for word in [u"确认", u"好", u"是", u"OK"]):
20 | mic.say('授权成功,开始进行相关操作', cache=True)
21 | time.sleep(3)
22 | subprocess.Popen("shutdown -h now", shell=True)
23 | return
24 | mic.say('授权失败,操作已取消,请重新尝试', cache=True)
25 | except Exception, e:
26 | logger.error(e)
27 | mic.say('抱歉,关闭系统失败', cache=True)
28 |
29 | def isValid(text):
30 | return any(word in text for word in [u"关机", u"关闭系统"])
31 |
--------------------------------------------------------------------------------
/HeadlineNews.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # 新闻插件
3 | import sys
4 | import os
5 | import logging
6 | import json, urllib
7 | from urllib import urlencode
8 |
9 | reload(sys)
10 | sys.setdefaultencoding('utf8')
11 |
12 | WORDS = ["XINWEN"]
13 | SLUG = "headline_news"
14 |
15 | def request(appkey,type, mic, logger, m="GET"):
16 | url = "http://v.juhe.cn/toutiao/index"
17 | params = {
18 | "key" : appkey,
19 | "type" : type[1]
20 | }
21 | params = urlencode(params)
22 | if m == "GET":
23 | f = urllib.urlopen("%s?%s" % (url, params))
24 | else:
25 | f = urllib.urlopen(url, params)
26 |
27 | content = f.read()
28 | res = json.loads(content)
29 | if res:
30 | error_code = res["error_code"]
31 | if error_code == 0:
32 | mic.say(type[0] + u"新闻", cache=True)
33 | limit = 5;
34 | news = res["result"]["data"][0:limit]
35 | news_for_tts = ""
36 | for new in news:
37 | news_for_tts = news_for_tts + new["title"] + "."
38 | mic.say(news_for_tts, cache=True)
39 | else:
40 | logger.error(str(error_code) + ':' + res["reason"])
41 | mic.say(res["reason"], cache=True)
42 | else:
43 | mic.say(u"新闻接口调用错误", cache=True)
44 |
45 | def getNewsType(text):
46 | newsTypes = {"头条":"top", "社会":"shehui","国内":"guonei", "国际":"guoji", "娱乐":"yule",
47 | "体育":"tiyu", "军事":"junshi", "科技":"keji","财经":"caijing","时尚":"shishang"}
48 | newsType = ["头条","top"]
49 | for type in newsTypes:
50 | if type in text:
51 | newsType = [type,newsTypes[type]]
52 | return newsType
53 |
54 | def handle(text, mic, profile, wxbot=None):
55 | logger = logging.getLogger(__name__)
56 |
57 | if SLUG not in profile or \
58 | 'key' not in profile[SLUG]:
59 | mic.say(u"新闻插件配置有误,插件使用失败", cache=True)
60 | return
61 | key = profile[SLUG]['key']
62 | type = getNewsType(text)
63 | request(key,type, mic, logger)
64 |
65 | def isValid(text):
66 | return u"新闻" in text
67 |
--------------------------------------------------------------------------------
/Lamp.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*- # 台灯控制
2 | import sys
3 | import os
4 | import logging
5 | import wiringpi
6 |
7 | sys.setdefaultencoding('utf8')
8 |
9 | SLUG="Lamp"
10 |
11 | WORDS=["TAIDENG"]
12 |
13 | def handle(text, mic, profile, wxbot=None):
14 |
15 | logger = logging.getLogger(__name__)
16 | # get config
17 | pin=profile[SLUG]['pin']
18 | wiringpi.wiringPiSetupPhys()
19 | wiringpi.pinMode(pin,1)
20 |
21 | if any(word in text for word in [u"打开",u"开启"]):
22 | wiringpi.digitalWrite(pin,0)
23 | mic.say("好的,已经打开台灯")
24 | elif any(word in text for word in [u"关闭",u"关掉",u"熄灭"]):
25 | wiringpi.digitalWrite(pin,1)
26 | mic.say("好的,已经关闭台灯")
27 | return True
28 |
29 |
30 | def isValid(text):
31 | """
32 | Returns True if the input is related to weather.
33 | Arguments:
34 | text -- user-input, typically transcribed speech
35 | """
36 |
37 | return u"台灯" in text
38 |
--------------------------------------------------------------------------------
/Lock.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 关闭系统插件
3 | import logging
4 | import sys
5 | import time
6 | import subprocess
7 | import httplib
8 |
9 | reload(sys)
10 | sys.setdefaultencoding('utf8')
11 |
12 | WORDS = ["GUANSUO"]
13 | SLUG = "Lock"
14 |
15 | def handle(text, mic, profile, wxbot=None):
16 | logger = logging.getLogger(__name__)
17 | try:
18 | mic.say('将要关闭车锁', cache=True)
19 | mic.say('三二一', cache=True)
20 | time.sleep(1)
21 |
22 | http_client=httplib.HTTPConnection('http://',80,timeout=20)
23 | http_client.request('GET','')
24 | r=http_client.getresponse()
25 | print r.status
26 | print r.read()
27 |
28 | mic.say('关锁成功', cache=True)
29 | except Exception, e:
30 | logger.error(e)
31 | mic.say('抱歉,关锁失败', cache=True)
32 |
33 | def isValid(text):
34 | return any(word in text for word in [u"关锁", u"关闭车锁"])
35 |
--------------------------------------------------------------------------------
/Locker:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 关闭系统插件
3 | import logging
4 | import sys
5 | import time
6 | import subprocess
7 | import httplib
8 |
9 | reload(sys)
10 | sys.setdefaultencoding('utf8')
11 |
12 | WORDS = ["GUANDENG"]
13 | SLUG = "halt"
14 |
15 | def handle(text, mic, profile, wxbot=None):
16 | logger = logging.getLogger(__name__)
17 | try:
18 | mic.say('将要关闭系统', cache=True)
19 | # input = mic.activeListen(MUSIC=True)
20 | # if input is not None and any(word in input for word in [u"确认", u"好", u"是", u"OK"]):
21 | mic.say('授权成功,开始连接百度', cache=True)
22 | time.sleep(3)
23 |
24 | http_client=httplib.HTTPConnection('baidu.com',80,timeout=20)
25 | http_client.request('GET','')
26 | r=http_client.getresponse()
27 | print r.status
28 | print r.read()
29 | # subprocess.Popen("shutdown -h now", shell=True)
30 | # return
31 | # mic.say('授权失败,操作已取消,请重新尝试', cache=True)
32 | except Exception, e:
33 | logger.error(e)
34 | mic.say('抱歉,关闭系统失败', cache=True)
35 |
36 | def isValid(text):
37 | return any(word in text for word in [u"关机", u"关闭系统"])
38 |
--------------------------------------------------------------------------------
/NetEaseMusic.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 网易云音乐播放插件
3 | import logging
4 | import threading
5 | import hashlib
6 | import time
7 | import subprocess
8 | import sys
9 | import os
10 | import re
11 | import random
12 | from MusicBoxApi import api as NetEaseApi
13 | import eyed3
14 | from random import shuffle
15 |
16 | reload(sys)
17 | sys.setdefaultencoding('utf8')
18 |
19 | #import cProfile, pstats, StringIO
20 |
21 | # Standard module stuff
22 | WORDS = ["YINYUE"]
23 | SLUG = "netease_music"
24 |
25 |
26 | def handle(text, mic, profile, wxbot=None):
27 | """
28 | Responds to user-input, typically speech text
29 |
30 | Arguments:
31 | text -- user-input, typically transcribed speech
32 | mic -- used to interact with the user (for both input and output)
33 | profile -- contains information related to the user (e.g., phone
34 | number)
35 | wxbot -- wechat bot instance
36 | """
37 | logger = logging.getLogger(__name__)
38 |
39 | kwargs = {}
40 | kwargs['mic'] = mic
41 | kwargs['profile'] = profile
42 |
43 | logger.debug("Preparing to start netease music module")
44 | try:
45 | netease_wrapper = NetEaseWrapper(**kwargs)
46 | except Exception, e:
47 | logger.debug(e)
48 | logger.error("Couldn't connect to NetEase server", exc_info=True)
49 | mic.say(u"访问网易云音乐失败了,请稍后再试", cache=True)
50 | return
51 |
52 | persona = 'DINGDANG'
53 | if 'robot_name' in profile:
54 | persona = profile['robot_name']
55 |
56 | robot_name_cn = u'叮当'
57 | if 'robot_name_cn' in profile:
58 | robot_name_cn = profile['robot_name_cn']
59 |
60 | logger.debug("Starting music mode")
61 |
62 | music_mode = MusicMode(persona, robot_name_cn, mic, netease_wrapper, wxbot)
63 | music_mode.stop = False
64 |
65 | # 登录网易云音乐
66 | account = ''
67 | password = ''
68 | report = False
69 | local_default = False
70 | if SLUG in profile:
71 | if 'account' in profile[SLUG]:
72 | account = profile[SLUG]['account']
73 | if 'password' in profile[SLUG]:
74 | password = profile[SLUG]['password']
75 | if 'report' in profile[SLUG]:
76 | report = profile[SLUG]['report']
77 | if 'local_default' in profile[SLUG]:
78 | local_default = profile[SLUG]['local_default']
79 | if account == '' or password == '':
80 | mic.say("请先配置好账户信息再找我播放音乐", cache=True)
81 | return
82 |
83 | has_login = False
84 | home_dir = os.path.expandvars('$HOME')
85 | user_info = os.path.join(home_dir, 'userInfo')
86 | if not (os.path.exists(user_info)):
87 | mic.say("稍等,正在为您登录网易云音乐", cache=True)
88 | res = music_mode.login(account, password, report)
89 | if res:
90 | mic.say("登录成功", cache=True)
91 | has_login = True
92 | else:
93 | music_mode.read_login_info(user_info, report)
94 | has_login = True
95 |
96 | if not has_login:
97 | mic.say("登录失败, 退出播放. 请检查配置, 稍后再试", cache=True)
98 | return
99 |
100 | if wxbot is not None:
101 | wxbot.music_mode = music_mode
102 |
103 | pattern = re.compile(ur'(我想听|来一首)([,]?)([\u4e00-\u9fa5]*)')
104 | text_utf8 = text.decode('utf-8')
105 | if pattern.match(text_utf8) and text != u'播放音乐':
106 | m = pattern.search(text_utf8)
107 | song_name = m.group(3)
108 | if song_name != '':
109 | music_mode.handleForever(
110 | play_type=2, song_name=song_name) # 2: 播放指定歌曲
111 | elif any(word in text for word in [u"歌单", u"我的"]):
112 | music_mode.handleForever(play_type=1) # 1: 用户歌单
113 | else:
114 | # 默认播放本地音乐
115 | if local_default:
116 | music_mode.handleForever(play_type=3) # 3: 本地音乐
117 | else:
118 | music_mode.handleForever(play_type=0) # 0: 推荐榜单
119 | logger.debug("Exiting music mode")
120 | return
121 |
122 |
123 | def isValid(text):
124 | """
125 | Returns True if the input is related to music.
126 |
127 | Arguments:
128 | text -- user-input, typically transcribed speech
129 | """
130 | return any(word in text for word in [u"听歌", u"音乐", u"播放",
131 | u"我想听", u"唱歌", u"唱首歌",
132 | u"歌单", u"榜单"])
133 |
134 |
135 | # The interesting part
136 | class MusicMode(object):
137 |
138 | def __init__(self, PERSONA, robot_name_cn, mic,
139 | netease_wrapper, wxbot=None):
140 | self._logger = logging.getLogger(__name__)
141 | self.persona = PERSONA
142 | self.robot_name_cn = robot_name_cn
143 | self.music = netease_wrapper
144 | self.mic = mic
145 | self.wxbot = wxbot
146 | self.search_mode = False
147 | self.to_listen = True
148 | self.to_report = False
149 | self.delegating = False
150 | if self.wxbot is not None:
151 | self.msg_thread = threading.Thread(target=self.wxbot.proc_msg)
152 |
153 | def read_login_info(self, user_info, report=False):
154 | self.to_report = report
155 | self.music.read_login_info(user_info)
156 |
157 | def login(self, account, password, report=False):
158 | self.to_report = report
159 | return self.music.login(account, password)
160 |
161 | def delegateInput(self, input, call_by_wechat=False):
162 |
163 | command = input.upper()
164 | if command.startswith(self.robot_name_cn + ": "):
165 | return
166 |
167 | if call_by_wechat:
168 | self._logger.debug('called by wechat')
169 | self.music.stop()
170 | time.sleep(.1)
171 |
172 | # check if input is meant to start the music module
173 | if u"榜单" in command:
174 | self.mic.say(u"播放榜单音乐", cache=True)
175 | self.music.update_playlist_by_type(0)
176 | self.music.play(self.to_report)
177 | return
178 | elif u"歌单" in command:
179 | self.music.update_playlist_by_type(1)
180 | self.music.play(self.to_report)
181 | return
182 | elif any(ext in command for ext in [u"本地", u"本机"]):
183 | self.music.update_playlist_by_type(3)
184 | self.music.play(self.to_report)
185 | return
186 | elif any(ext in command for ext in [u"停止聆听", u"关闭聆听", u"别听我的"]):
187 | if self.wxbot is None or not self.wxbot.is_login:
188 | self.mic.say(u"您还未登录微信,不能关闭语音交互功能", cache=True)
189 | return
190 | self.mic.say(u"关闭语音交互功能", cache=True)
191 | self.to_listen = False
192 | self.music.play(False)
193 | return
194 | elif any(ext in command for ext in [
195 | u"恢复聆听", u"开始聆听", u"开启聆听", u"听我的"]):
196 | self.mic.say(u"开启语音交互功能", cache=True)
197 | self.to_listen = True
198 | self.music.play(False)
199 | return
200 | elif u"暂停" in command:
201 | self.mic.say(u"暂停播放", cache=True)
202 | self.music.pause()
203 | return
204 | elif any(ext in command for ext in [u"结束", u"退出", u"停止"]):
205 | self.music.exit()
206 | self.mic.say(u"结束播放", cache=True)
207 | if self.wxbot is not None:
208 | self.wxbot.music_mode = None
209 | return
210 | elif any(ext in command for ext in [u"大声", u"大声点", u"大点声"]):
211 | self.mic.say(u"大点声", cache=True)
212 | self.music.increase_volume()
213 | return
214 | elif any(ext in command for ext in [u"小声", u"小点声", u"小声点"]):
215 | self.mic.say(u"小点声", cache=True)
216 | self.music.decrease_volume()
217 | return
218 | elif any(
219 | ext in command for ext in [
220 | u'下一首', u"下首歌", u"切歌",
221 | u"下一首歌", u"换首歌", u"切割",
222 | u"那首歌"]):
223 | self.mic.say(u"下一首歌", cache=True)
224 | self.music.next()
225 | return
226 | elif any(ext in command for ext in [u'上一首', u'上一首歌', u'上首歌']):
227 | self.mic.say(u"上一首歌", cache=True)
228 | self.music.previous()
229 | return
230 | elif any(ext in command for ext in [u'搜索', u'查找']):
231 | if call_by_wechat:
232 | self.search_mode = True
233 | self.mic.say(u"请直接回复要搜索的关键词", cache=True)
234 | return
235 | else:
236 | self.mic.say(u"请在滴一声后告诉我您要搜索的关键词", cache=True)
237 | input = self.mic.activeListen(MUSIC=True)
238 | if input is None or input.strip() == '':
239 | self.mic.say("没有听到关键词呢,请重新叫我查找吧", cache=True)
240 | self.music.play(False)
241 | return
242 | self.mic.say(u'正在为您搜索%s' % input)
243 | self.music.update_playlist_by_type(2, input)
244 | self.music.play(self.to_report)
245 | return
246 | elif u'什么歌' in command:
247 | self.mic.say(u"正在播放的是%s的%s" % (
248 | self.music.song['artist'],
249 | self.music.song['song_name']))
250 | self.music.play(False)
251 | return
252 | elif u'随机' in command:
253 | self.mic.say(u"随机播放", cache=True)
254 | self.music.randomize()
255 | return
256 | elif u'顺序' in command:
257 | self.mic.say(u"顺序播放", cache=True)
258 | self.music.serialize()
259 | return
260 | elif any(ext in command for ext in [u"播放", u"继续", u"我想听", u"来一首"]):
261 | pattern = re.compile(ur'(播放|我想听|来一首)([,]?)([\u4e00-\u9fa5]+)')
262 | text_utf8 = command.decode('utf-8')
263 | song_name = ''
264 | if pattern.match(text_utf8):
265 | m = pattern.search(text_utf8)
266 | song_name = m.group(3)
267 | if song_name != '':
268 | self.music.update_playlist_by_type(2, song_name)
269 | elif u'即将播放' not in command:
270 | self.music.play(self.to_report)
271 | return
272 | elif self.search_mode:
273 | self.search_mode = False
274 | input = command
275 | if input is None or input.strip() == '':
276 | self.mic.say("没有听到关键词呢,请重新叫我查找吧", cache=True)
277 | self.music.play(False)
278 | return
279 | self.mic.say(u'正在为您搜索%s' % input)
280 | self.music.update_playlist_by_type(2, input)
281 | self.music.play(self.to_report)
282 | else:
283 | self.mic.say(u"没有听懂呢。要退出播放,请说退出播放", cache=True)
284 | self.music.play(False)
285 | return
286 | return
287 |
288 | def handleForever(self, play_type=0, song_name=''):
289 | """
290 | 进入音乐播放
291 | play_type - 0:播放推荐榜单;1:播放用户歌单
292 | """
293 | if song_name != '':
294 | self.music.update_playlist_by_type(2, song_name)
295 | else:
296 | self.music.update_playlist_by_type(play_type)
297 | self.music.start()
298 | if self.wxbot is not None:
299 | self.msg_thread.start()
300 | while True:
301 |
302 | if self.music.is_stop:
303 | self._logger.info('Stop Netease music mode')
304 | return
305 |
306 | if not self.to_listen or self.delegating:
307 | self._logger.info("Listening mode is disabled.")
308 | continue
309 |
310 | try:
311 | self._logger.info('离线唤醒监听中')
312 | threshold, transcribed = self.mic.passiveListen(self.persona)
313 | except Exception, e:
314 | self._logger.debug(e)
315 | threshold, transcribed = (None, None)
316 |
317 | if not transcribed or not threshold:
318 | self._logger.info("Nothing has been said or transcribed.")
319 | continue
320 |
321 | # 当听到呼叫机器人名字时,停止播放
322 | self.music.stop()
323 | time.sleep(.1)
324 |
325 | # 听用户说话
326 | input = self.mic.activeListen(MUSIC=True)
327 |
328 | if input:
329 | if any(ext in input for ext in [u"结束", u"退出", u"停止"]):
330 | self.mic.say(u"结束播放", cache=True)
331 | self.music.stop()
332 | self.music.exit()
333 | return
334 | if not self.delegating:
335 | self.delegating = True
336 | self.delegateInput(input)
337 | self.delegating = False
338 | else:
339 | self.mic.say(u"什么?", cache=True)
340 | if not self.music.is_pause:
341 | self.music.play(False)
342 |
343 |
344 | class NetEaseWrapper(threading.Thread):
345 |
346 | def __init__(self, mic, profile):
347 | super(NetEaseWrapper, self).__init__()
348 | self.logger = logging.getLogger(__name__)
349 | self.cond = threading.Condition()
350 | self.netease = NetEaseApi.NetEase()
351 | self.mic = mic
352 | self.profile = profile
353 | self.userId = ""
354 | self.volume = 0.7
355 | self.song = None # 正在播放的曲目信息
356 | self.idx = -1 # 正在播放的曲目序号
357 | self.random = False
358 | self.playlist = []
359 | self.is_pause = False
360 | self.is_stop = False
361 |
362 | def set_cond(self, cond):
363 | self.cond = cond
364 |
365 | def update_playlist_by_type(self, play_type, keyword=''):
366 | if play_type == 0:
367 | # 播放热门榜单音乐
368 | self.playlist = self.get_top_songlist()
369 | elif play_type == 1:
370 | # 播放用户歌单
371 | user_playlist = self.get_user_playlist()
372 | if user_playlist > 0:
373 | self.playlist = self.get_song_list_by_playlist_id(
374 | user_playlist[0]['id'])
375 | if len(self.playlist) == 0:
376 | self.mic.say("用户歌单没有歌曲,改为播放推荐榜单", cache=True)
377 | self.playlist = self.get_top_songlist()
378 | else:
379 | self.mic.say("当前用户没有歌单,改为播放推荐榜单", cache=True)
380 | self.playlist = self.get_top_songlist()
381 | elif play_type == 2:
382 | # 搜索歌曲
383 | self.playlist = self.search_by_name(keyword)
384 | elif play_type == 3:
385 | self.playlist = self.get_local_songlist()
386 |
387 | def get_local_songlist(self): # 本地音乐
388 | local_path = ''
389 | if 'local_path' in self.profile[SLUG]:
390 | local_path = self.profile[SLUG]['local_path']
391 |
392 | playlist = []
393 | for (dirpath, dirnames, filenames) in os.walk(local_path):
394 | # f.extend(filenames)
395 | for filename in filenames:
396 | try:
397 | # only mp3 accept
398 | if os.path.splitext(filename)[1] != ".mp3":
399 | continue
400 | # read mp3 properties and add to the playlist
401 | mp3_path = dirpath + filename
402 | audiofile = eyed3.load(mp3_path)
403 | music_info = {}
404 | if audiofile.tag:
405 | if audiofile.tag.title:
406 | music_info.setdefault(
407 | "song_name", audiofile.tag.title)
408 | music_info.setdefault("artist", audiofile.tag.artist)
409 | else:
410 | music_info.setdefault(
411 | "song_name", os.path.splitext(filename)[0])
412 | music_info.setdefault("artist", "")
413 | music_info.setdefault("song_id", "0")
414 | music_info.setdefault("album_name", "")
415 | music_info.setdefault("mp3_url", "'{}'".format(mp3_path))
416 | music_info.setdefault("playTime", int(
417 | audiofile.info.time_secs) * 1000)
418 | music_info.setdefault("quality", "")
419 | playlist.append(music_info)
420 | except Exception, e:
421 | self.logger.error(e)
422 | pass
423 | break
424 | # 随机本地音乐列表顺序
425 | if 'local_shuffle' in self.profile[SLUG]:
426 | if self.profile[SLUG]['local_shuffle']:
427 | shuffle(playlist)
428 | return playlist
429 |
430 | def get_top_songlist(self): # 热门单曲
431 | music_list = self.netease.top_songlist()
432 | datalist = self.netease.dig_info(music_list, 'songs')
433 | playlist = []
434 | for data in datalist:
435 | music_info = {}
436 | music_info.setdefault("song_id", data.get("song_id"))
437 | music_info.setdefault("song_name", data.get("song_name"))
438 | music_info.setdefault("artist", data.get("artist"))
439 | music_info.setdefault("album_name", data.get("album_name"))
440 | music_info.setdefault("mp3_url", data.get("mp3_url"))
441 | music_info.setdefault("playTime", data.get("playTime"))
442 | music_info.setdefault("quality", data.get("quality"))
443 | playlist.append(music_info)
444 | return playlist
445 |
446 | def read_login_info(self, user_info):
447 | assert(os.path.exists(user_info))
448 | with open(user_info) as f:
449 | self.userId = f.readline()
450 |
451 | def login(self, username, password): # 用户登陆
452 | password = hashlib.md5(password).hexdigest()
453 | login_info = self.netease.login(username, password)
454 | if login_info['code'] == 200:
455 | res = True
456 | userId = login_info.get('profile').get('userId')
457 | self.userId = userId
458 | home_dir = os.path.expandvars('$HOME')
459 | user_info = os.path.join(home_dir, 'userInfo')
460 | file = open(user_info, 'w')
461 | file.write(str(userId))
462 | file.close()
463 | else:
464 | res = False
465 | return res
466 |
467 | def get_user_playlist(self): # 获取用户歌单
468 | play_list = self.netease.user_playlist(self.userId) # 用户歌单
469 | return play_list
470 |
471 | def get_song_list_by_playlist_id(self, playlist_id):
472 | songs = self.netease.playlist_detail(playlist_id)
473 | song_list = self.netease.dig_info(songs, 'songs')
474 | return song_list
475 |
476 | def search_by_name(self, song_name):
477 | data = self.netease.search(song_name)
478 | song_ids = []
479 | if 'songs' in data['result']:
480 | if 'mp3Url' in data['result']['songs']:
481 | songs = data['result']['songs']
482 |
483 | else:
484 | for i in range(0, len(data['result']['songs'])):
485 | song_ids.append(data['result']['songs'][i]['id'])
486 | songs = self.netease.songs_detail(song_ids)
487 | song_list = self.netease.dig_info(songs, 'songs')
488 | return song_list
489 |
490 | def current_song(self):
491 | if self.song is not None:
492 | return self.song['song_name']
493 | else:
494 | return ''
495 |
496 | def run(self):
497 | while True:
498 | if self.cond.acquire():
499 | self.play()
500 | self.pick_next()
501 |
502 | def play(self, report=False):
503 | if self.is_pause:
504 | self.is_pause = False
505 | if self.idx < len(self.playlist):
506 | if self.idx == -1:
507 | self.idx = 0
508 | if not self.random:
509 | song = self.playlist[self.idx]
510 | else:
511 | song = random.choice(self.playlist)
512 | self.song = song
513 | subprocess.Popen("pkill play", shell=True)
514 | # pr = cProfile.Profile()
515 | # pr.enable()
516 | # if song['mp3_url'] contains 'http://',
517 | # means music from internet, not local
518 | if "http://" in song['mp3_url']:
519 | song['mp3_url'] = self.netease.songs_detail_new_api(
520 | [song['song_id']])[0]['url']
521 | # pr.disable()
522 | # s = StringIO.StringIO()
523 | # sortby = 'cumulative'
524 | # ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
525 | # ps.print_stats()
526 | # print s.getvalue()
527 | mp3_url = song['mp3_url']
528 | if mp3_url is None:
529 | self.next()
530 | self.cond.wait()
531 | try:
532 | if report:
533 | self.mic.say(u"即将播放:%s,%s" % (
534 | song['artist'], song['song_name']))
535 | time.sleep(.1)
536 | subprocess.Popen("play -v %f %s" % (
537 | self.volume, mp3_url), shell=True, stdout=subprocess.PIPE)
538 | self.cond.notify()
539 | self.cond.wait(int(song.get('playTime')) / 1000)
540 | except Exception:
541 | pass
542 | else:
543 | try:
544 | subprocess.Popen("pkill play", shell=True)
545 | self.cond.notify()
546 | self.cond.wait()
547 | except Exception:
548 | pass
549 |
550 | def notify(self):
551 | if self.cond.acquire():
552 | self.cond.notifyAll()
553 | self.cond.release()
554 |
555 | def previous(self):
556 | self.idx -= 2
557 | if self.idx < 0:
558 | self.idx = len(self.playlist) - 1
559 | self.notify()
560 |
561 | def pick_next(self):
562 | self.idx += 1
563 | if self.idx > len(self.playlist) - 1:
564 | self.idx = 0
565 |
566 | def next(self):
567 | self.notify()
568 |
569 | def randomize(self):
570 | self.random = True
571 | self.next()
572 |
573 | def serialize(self):
574 | self.random = False
575 | self.notify()
576 |
577 | def increase_volume(self):
578 | self.volume += .1
579 | if self.volume > 1:
580 | self.volume = 1
581 | self.notify()
582 |
583 | def decrease_volume(self):
584 | self.volume -= .1
585 | if self.volume < 0:
586 | self.volume = 0
587 | self.notify()
588 |
589 | def stop(self):
590 | try:
591 | subprocess.Popen("pkill play", shell=True)
592 | self.cond.notifyAll()
593 | self.cond.release()
594 | self.cond.wait()
595 | except Exception:
596 | pass
597 |
598 | def pause(self):
599 | self.is_pause = True
600 | # 暂不支持断点续播,因此暂停和停止相同处理
601 | self.stop()
602 |
603 | def exit(self):
604 | self.is_stop = True
605 | self.playlist = []
606 | self.notify()
607 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dingdang-contrib
2 |
3 | [叮当机器人](http://github.com/wzpan/dingdang) 的用户贡献插件。
4 |
5 | * [插件列表](https://github.com/dingdang-robot/dingdang-contrib/wiki)
6 |
7 | ## 安装
8 |
9 | ``` sh
10 | cd /home/pi/.dingdang
11 | git clone http://github.com/dingdang-robot/dingdang-contrib contrib
12 | pip install -r contrib/requirements.txt
13 | ```
14 |
15 | ## 升级
16 |
17 | ``` sh
18 | cd /home/pi/.dingdang/contrib
19 | git pull
20 | pip install --upgrade -r requirements.txt
21 | ```
22 |
23 | ## 如何贡献
24 |
25 | ### 教程
26 |
27 | [手把手教你编写叮当机器人插件](http://www.hahack.com/codes/how-to-write-dingdang-plugin/)
28 |
29 | ### 流程说明
30 |
31 | 1. fork 本项目;
32 | 2. 添加你的插件;
33 | 3. 如果你的插件有依赖第三方库,将依赖项添加进 requirements.txt 。如果依赖第三方工具, 则在 README 中的[安装](#安装) 一节中补充。
34 | 4. 发起 pull request ,说明该插件的用途、指令和配置信息(如果有的话)。
35 | 5. pull request 被 accept 后,在本项目 [Wiki页](https://github.com/dingdang-robot/dingdang-contrib/wiki/neteasemusic) 中添加一项,将该插件的用途、指令和配置信息也添加到此页面中。
36 |
37 | ### 插件规范
38 |
39 | * 每个插件只能包含一个 .py 文件。依赖的其他库文件不得放进本仓库中,否则会被当成插件而解析出错。如果确实需要分成多个文件,可以将其他文件打包发布到 PyPi ,然后将依赖加进 requirements.txt 中。
40 |
--------------------------------------------------------------------------------
/RaspberryPiStatus.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 树莓派状态插件
3 | import sys
4 | import os
5 | import logging
6 |
7 | reload(sys)
8 | sys.setdefaultencoding('utf8')
9 |
10 | WORDS = ["SHUMEIPAIZHUANGTAI"]
11 | SLUG = "pi_status"
12 |
13 | def getCPUtemperature(logger, mic):
14 | result = 0.0
15 | try:
16 | tempFile = open("/sys/class/thermal/thermal_zone0/temp")
17 | res = tempFile.read()
18 | result = float(res) / 1000
19 | except:
20 | logger.error(e)
21 | mic.say(u'抱歉,无法获取处理器温度', cache=True)
22 | return result
23 |
24 | def getRAMinfo():
25 | p = os.popen('free')
26 | i = 0
27 | while 1:
28 | i = i + 1
29 | line = p.readline()
30 | if i == 2:
31 | return (line.split()[1:4])
32 |
33 | def getDiskSpace():
34 | p = os.popen("df -h /")
35 | i = 0
36 | while 1:
37 | i = i +1
38 | line = p.readline()
39 | if i == 2:
40 | return (line.split()[1:5])
41 |
42 | def getPiStatus(logger, mic):
43 | result = {'cpu_tmp': 0.0,
44 | 'ram_total': 0, 'ram_used': 0, 'ram_percentage': 0,
45 | 'disk_total': '0.0', 'disk_used': '0.0','disk_percentage': 0}
46 |
47 | result['cpu_tmp'] = getCPUtemperature(logger, mic)
48 | ram_stats = getRAMinfo()
49 | result['ram_total'] = int(ram_stats[0]) / 1024
50 | result['ram_used'] = int(ram_stats[1]) / 1024
51 | result['ram_percentage'] = int(result['ram_used'] * 100 / result['ram_total'])
52 | disk_stats = getDiskSpace()
53 | result['disk_total'] = disk_stats[0]
54 | result['disk_used'] = disk_stats[1]
55 | result['disk_percentage'] = disk_stats[3].split('%')[0]
56 | return result
57 |
58 | def handle(text, mic, profile, wxbot=None):
59 | logger = logging.getLogger(__name__)
60 | try:
61 | status = getPiStatus(logger, mic)
62 | mic.say(u'处理器温度' + str(status['cpu_tmp']) + u'度,内存使用百分之' + str(status['ram_percentage']) + u',存储使用百分之' + str(status['disk_percentage']))
63 | except Exception, e:
64 | logger.error(e)
65 | mic.say(u'抱歉,我没有获取到树莓派状态', cache=True)
66 |
67 | def isValid(text):
68 | return any(word in text for word in [u"树莓派状态", u"状态", u"运行状态"])
69 |
--------------------------------------------------------------------------------
/Reboot.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 重启系统插件
3 | import logging
4 | import sys
5 | import time
6 | import subprocess
7 |
8 | reload(sys)
9 | sys.setdefaultencoding('utf8')
10 |
11 | WORDS = ["CHONGQI"]
12 | SLUG = "reboot"
13 |
14 | def handle(text, mic, profile, wxbot=None):
15 | logger = logging.getLogger(__name__)
16 | try:
17 | mic.say('将要重新启动系统,请在滴一声后进行确认,授权相关操作', cache=True)
18 | input = mic.activeListen(MUSIC=True)
19 | if input is not None and any(word in input for word in [u"确认", u"好", u"是", u"OK"]):
20 | mic.say('授权成功,开始进行相关操作', cache=True)
21 | time.sleep(3)
22 | subprocess.Popen("reboot -f", shell=True)
23 | return
24 | mic.say('授权失败,操作已取消,请重新尝试', cache=True)
25 | except Exception, e:
26 | logger.error(e)
27 | mic.say('抱歉,重新启动系统失败', cache=True)
28 |
29 | def isValid(text):
30 | return any(word in text for word in [u"重启", u"重新启动"])
31 |
--------------------------------------------------------------------------------
/RoadCondition.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | import sys
3 | import os
4 | import logging
5 | import json
6 | import requests
7 |
8 | WORDS = ["LUKUANG"]
9 | SLUG = "roadcondition"
10 |
11 | def request(url, params):
12 | result = requests.post(url, data=params)
13 | return json.loads(result.text, encoding='utf-8')
14 |
15 |
16 | def handle(text, mic, profile, wxbot=None):
17 | logger = logging.getLogger(__name__)
18 |
19 | if SLUG not in profile or \
20 | 'app_key' not in profile[SLUG] or \
21 | 'adcode' not in profile[SLUG]:
22 | mic.say(u"插件配置有误,插件使用失败")
23 | return
24 |
25 | app_key = profile[SLUG]['app_key']
26 | adcode = profile[SLUG]['adcode']
27 | mic.say(u'哪条道路')
28 | input = mic.activeListen(MUSIC=True)
29 | if input is None:
30 | input = "龙岗大道"
31 |
32 |
33 | url_transit = "http://restapi.amap.com/v3/traffic/status/road"
34 | params = {"adcode" : adcode,"name" : input,"key" : app_key}
35 |
36 | res = request(url_transit,params)
37 | print res
38 | if res:
39 | status = res["status"]
40 | if status == "1":
41 | print "status == 1"
42 | print res['trafficinfo']
43 | if len(res['trafficinfo']) > 0:
44 | trafficinfo = res['trafficinfo']['evaluation']['description']
45 | trafficinfo1 = res['trafficinfo']['description']
46 | mic.say(trafficinfo)
47 | mic.say(trafficinfo1)
48 | else:
49 | mic.say(u"无法获取到信息")
50 | return
51 | else:
52 | logger.error(u"接口错误:")
53 | return
54 | else:
55 | logger.error(u"接口调用失败")
56 | return
57 |
58 |
59 | def isValid(text):
60 | return any(word in text for word in [u"路况"])
61 |
--------------------------------------------------------------------------------
/SendMessage.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 向微信好友发消息插件
3 | import logging
4 | import sys
5 | import re
6 | import time
7 |
8 | reload(sys)
9 | sys.setdefaultencoding('utf8')
10 |
11 | WORDS = ["FAXIN"]
12 | SLUG = "send_message"
13 |
14 | def handle(text, mic, profile, wxbot=None):
15 | logger = logging.getLogger(__name__)
16 | try:
17 | text_utf8 = text.decode('utf8')
18 | # 匹配规则:[给|向] XXX(微信昵称) [发信|发信息|发送信息|发送消息|发消息] 说 XXX(消息内容)
19 | # 微信昵称 -- 中文,名称2-15位长度; 消息内容 -- 1-25位长度
20 | PATTERN = ur'(给|向)([\u4e00-\u9fa5]{2,15})(发信|发送|发消息)(\S+)(说)(\S+)'
21 | pattern = re.compile(PATTERN)
22 | m = pattern.search(text_utf8)
23 | if not m or m.lastindex < 3:
24 | mic.say('抱歉,没能识别联系人,请重试', cache=True)
25 | return;
26 | username = m.group(2)
27 | mic.say('好嘞,开始给%s送信' % (username), cache=True)
28 | time.sleep(.3)
29 | if m.lastindex < 6:
30 | mic.say('抱歉,没有听清楚消息内容', cache=True)
31 | return;
32 | msgbody = m.group(6)
33 | comfirm_input = u'确认'
34 | confirm_message_body = True
35 | if SLUG in profile:
36 | if 'confirm_message_body' in profile[SLUG]:
37 | confirm_message_body = profile[SLUG]['confirm_message_body']
38 | if confirm_message_body:
39 | mic.say('将要提交消息,消息内容是:%s,请在滴一声后确认' % (msgbody))
40 | comfirm_input = mic.activeListen(MUSIC=True)
41 | if comfirm_input is not None and any(word in comfirm_input for word in [u"确认", u"好", u"是", u"OK"]):
42 | wxbot.send_msg(username, msgbody, False)
43 | mic.say('提交成功,消息内容:%s' % (msgbody), cache=True)
44 | return
45 | mic.say('确认失败,操作已取消,请重新尝试', cache=True)
46 | except Exception, e:
47 | logger.error(e)
48 | mic.say('抱歉,消息没有提交成功', cache=True)
49 |
50 | def isValid(text):
51 | return any(word in text for word in [u"发信", u"发送", u"发消息"])
52 |
--------------------------------------------------------------------------------
/SpeakIP.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 获取IP插件
3 | import logging
4 | import sys
5 | import time
6 | import socket
7 | import subprocess
8 |
9 | reload(sys)
10 | sys.setdefaultencoding('utf8')
11 |
12 | WORDS = ["IP"]
13 | SLUG = "speak_ip"
14 |
15 | def getLocalIP():
16 | ip = None
17 | try:
18 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
19 | s.connect(('114.114.114.114', 0))
20 | ip = s.getsockname()[0]
21 | except:
22 | name = socket.gethostname()
23 | ip = socket.gethostbyname(name)
24 | if ip.startswith("127."):
25 | cmd = '''/sbin/ifconfig | grep "inet " | cut -d: -f2 | awk '{print $1}' | grep -v "^127."'''
26 | a = subprocess.Popen(
27 | cmd,
28 | shell=True,
29 | stdout=subprocess.PIPE,
30 | stderr=subprocess.PIPE)
31 | a.wait()
32 | out = a.communicate()
33 | ip = out[0].strip().split("\n") # 所有的列表
34 | if len(ip) == 1 and ip[0] == "" or len(ip) == 0:
35 | return False
36 | ip = '完毕'.join(ip)
37 | return ip
38 |
39 | def handle(text, mic, profile, wxbot=None):
40 | logger = logging.getLogger(__name__)
41 | try:
42 | count = 0
43 | while True:
44 | ip = getLocalIP()
45 | logger.debug('getLocalIP: ', ip)
46 | if ip == False:
47 | mic.say('正在获取中', cache=True)
48 | else:
49 | count += 1
50 | ip += '完毕'
51 | mic.say(ip, cache=True)
52 | if count == 1:
53 | break
54 | time.sleep(1)
55 | except Exception, e:
56 | logger.error(e)
57 | mic.say('抱歉,我没有获取到地址', cache=True)
58 |
59 | def isValid(text):
60 | return any(word in text for word in [u"IP", u"网络地址"])
61 |
--------------------------------------------------------------------------------
/SpeakTemperature.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 获取室温插件
3 | import logging
4 | import sys
5 | import time
6 | import socket
7 | import subprocess
8 | import RPi.GPIO as GPIO
9 |
10 | reload(sys)
11 | sys.setdefaultencoding('utf8')
12 |
13 | WORDS = ["SHIWEN"]
14 | SLUG = "speak_temperature"
15 |
16 |
17 | def getTempperature(temp):
18 | data = []
19 | j = 0
20 | channel =0 #输入GPIO号
21 | channel = int(temp)
22 | GPIO.setmode(GPIO.BCM)
23 | time.sleep(1)
24 | GPIO.setup(channel, GPIO.OUT)
25 | GPIO.output(channel, GPIO.LOW)
26 | time.sleep(0.02)
27 | GPIO.output(channel, GPIO.HIGH)
28 | GPIO.setup(channel, GPIO.IN)
29 |
30 | while GPIO.input(channel) == GPIO.LOW:
31 | continue
32 | while GPIO.input(channel) == GPIO.HIGH:
33 | continue
34 |
35 | while j < 40:
36 | k = 0
37 | while GPIO.input(channel) == GPIO.LOW:
38 | continue
39 | while GPIO.input(channel) == GPIO.HIGH:
40 | k += 1
41 | if k > 100:
42 | break
43 | if k < 8:
44 | data.append(0)
45 | else:
46 | data.append(1)
47 | j += 1
48 | print "sensor is working."
49 | print data
50 | humidity_bit = data[0:8]
51 | humidity_point_bit = data[8:16]
52 | temperature_bit = data[16:24]
53 | temperature_point_bit = data[24:32]
54 | check_bit = data[32:40]
55 | humidity = 0
56 | humidity_point = 0
57 | temperature = 0
58 | temperature_point = 0
59 | check = 0
60 |
61 | for i in range(8):
62 | humidity += humidity_bit[i] * 2 ** (7-i)
63 | humidity_point += humidity_point_bit[i] * 2 ** (7-i)
64 | temperature += temperature_bit[i] * 2 ** (7-i)
65 | temperature_point += temperature_point_bit[i] * 2 ** (7-i)
66 | check += check_bit[i] * 2 ** (7-i)
67 |
68 | tmp = humidity + humidity_point + temperature + temperature_point
69 |
70 | if check == tmp:
71 | print "temperature :", temperature, "*C, humidity :", humidity, "%"
72 | return "主人,当前家中温度"+str(temperature)+"摄氏度,湿度:百分之"+str(humidity)
73 | else:
74 | #print "wrong"
75 | #return "抱歉主人,传感器犯了点小错"
76 | getTempperature(channel)
77 | GPIO.cleanup()
78 |
79 | def handle(text, mic, profile, wxbot=None):
80 | logger = logging.getLogger(__name__)
81 | if SLUG not in profile or \
82 | 'gpio' not in profile[SLUG]:
83 | mic.say('DHT11配置有误,插件使用失败', cache=True)
84 | return
85 | if 'gpio' in profile[SLUG]:
86 | temp = profile[SLUG]['gpio']
87 | else:
88 | temp = profile['gpio']
89 | try:
90 | temper = getTempperature(temp)
91 | logger.debug('getTempperature: ', temper)
92 | mic.say(temper)
93 | except Exception, e:
94 | print "配置异常"
95 | logger.error(e)
96 | mic.say('抱歉,我没有获取到湿度', cache=True)
97 |
98 | def isValid(text):
99 | return any(word in text for word in [u"室温", u"家中温度"])
100 |
--------------------------------------------------------------------------------
/ToDo.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | import sys
3 | import os
4 | import logging
5 |
6 | reload(sys)
7 | sys.setdefaultencoding('utf8')
8 |
9 | WORDS = ["BEIWANG"]
10 | SLUG = "todo"
11 |
12 | def file_input(file_path):
13 | global flist
14 | global line_number
15 | global last_line
16 | f=open('%s' % file_path,'r')
17 | flist=f.readlines()
18 | n = 0
19 | for fline in flist:
20 | n += 1
21 | line_number = n
22 | last_line = fline
23 | last_line = last_line.rstrip('\n')
24 |
25 | def handle(text, mic, profile, wxbot=None):
26 | logger = logging.getLogger(__name__)
27 | if SLUG not in profile or \
28 | not profile[SLUG].has_key('file_path'):
29 | mic.say('备忘插件配置有误,插件使用失败', cache=True)
30 | return
31 | file_path = profile[SLUG]['file_path']
32 | if not os.path.isfile(file_path):
33 | os.system('touch %s' % file_path)
34 | os.system("sed -i '/^$/d' %s" % file_path)
35 | try:
36 | if any(word in text for word in [u"增加", u"添加"]):
37 | mic.say('请在滴一声后回复备忘内容', cache=True)
38 | input = mic.activeListen(MUSIC=True)
39 | if input is None:
40 | mic.say('抱歉,我没听清', cache=True)
41 | return
42 | else:
43 | os.system("echo %s >> %s" % (input, file_path))
44 | mic.say('好的,已添加备忘%s' % input, cache=True)
45 | elif any(word in text for word in [u"上", u"刚"]):
46 | try:
47 | file_input(file_path)
48 | except:
49 | mic.say('您还没有备忘', cache=True)
50 | return
51 | if any(word in text for word in [u"删除", u"清除"]):
52 | mic.say('即将删除上一条备忘%s,请在滴一声后进行确认' % last_line, cache=True)
53 | input = mic.activeListen(MUSIC=True)
54 | if input is not None and any(word in input for word in [u"确认", u"好", u"是", u"OK"]):
55 | os.system("sed -i '$d' %s" % file_path)
56 | mic.say('已删除上一条备忘', cache=True)
57 | else:
58 | mic.say('已取消', cache=True)
59 | else:
60 | mic.say('上一条备忘是:%s' % last_line, cache=True)
61 | else:
62 | try:
63 | file_input(file_path)
64 | except:
65 | mic.say('您还没有备忘', cache=True)
66 | return
67 | if any(word in text for word in [u"删除", u"清除", u"清空"]):
68 | mic.say('即将清空备忘,请在滴一声后进行确认', cache=True)
69 | input = mic.activeListen(MUSIC=True)
70 | if input is not None and any(word in input for word in [u"确认", u"好", u"是", u"OK"]):
71 | os.system('> %s' % file_path)
72 | mic.say('已清空备忘', cache=True)
73 | else:
74 | mic.say('已取消', cache=True)
75 | else:
76 | mic.say('您共有以下%d条备忘:' % line_number)
77 | for fline in flist:
78 | mic.say('%s' % fline)
79 | except Exception, e:
80 | logger.error(e)
81 | mic.say('抱歉,备忘插件出错', cache=True)
82 | def isValid(text):
83 | return any(word in text for word in [u"备忘"])
84 |
--------------------------------------------------------------------------------
/WOL.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | import socket, sys
3 | import struct
4 | import logging
5 |
6 | reload(sys)
7 | sys.setdefaultencoding('utf8')
8 |
9 | WORDS = ["KAIJI"]
10 | SLUG = "wol"
11 |
12 | def Waker(ip,mac):
13 | global sent
14 | def to_hex_int(s):
15 | return int(s.upper(), 16)
16 |
17 | dest = (ip, 9)
18 |
19 | spliter = ""
20 | if mac.count(":") == 5: spliter = ":"
21 | if mac.count("-") == 5: spliter = "-"
22 |
23 | parts = mac.split(spliter)
24 | a1 = to_hex_int(parts[0])
25 | a2 = to_hex_int(parts[1])
26 | a3 = to_hex_int(parts[2])
27 | a4 = to_hex_int(parts[3])
28 | a5 = to_hex_int(parts[4])
29 | a6 = to_hex_int(parts[5])
30 | addr = [a1, a2, a3, a4, a5, a6]
31 |
32 | packet = chr(255) + chr(255) + chr(255) + chr(255) + chr(255) + chr(255)
33 |
34 | for n in range(0,16):
35 | for a in addr:
36 | packet = packet + chr(a)
37 |
38 | packet = packet + chr(0) + chr(0) + chr(0) + chr(0) + chr(0) + chr(0)
39 |
40 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
41 | s.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)
42 | s.sendto(packet,dest)
43 |
44 | if len(packet) == 108:
45 | sent = True
46 |
47 | def handle(text, mic, profile, wxbot=None):
48 | logger = logging.getLogger(__name__)
49 | if SLUG not in profile or \
50 | not profile[SLUG].has_key('ip') or \
51 | not profile[SLUG].has_key('mac'):
52 | mic.say('WOL配置有误,插件使用失败', cache=True)
53 | return
54 | ip = profile[SLUG]['ip']
55 | mac = profile[SLUG]['mac']
56 | try:
57 | Waker(ip,mac)
58 | if sent:
59 | mic.say('启动成功', cache=True)
60 | except Exception, e:
61 | logger.error(e)
62 | mic.say('抱歉,启动失败', cache=True)
63 |
64 |
65 | def isValid(text):
66 | return any(word in text for word in [u"开机", u"启动电脑", u"开电脑"])
67 |
--------------------------------------------------------------------------------
/Weather.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # 天气插件
3 | import logging
4 | import requests
5 | import json
6 | import sys
7 |
8 | reload(sys)
9 | sys.setdefaultencoding('utf8')
10 |
11 | # Standard module stuff
12 | WORDS = ["TIANQI"]
13 | SLUG = "weather"
14 |
15 | def analyze_today(weather_code, suggestion):
16 | """ analyze today's weather """
17 | weather_code = int(weather_code)
18 | if weather_code <= 8:
19 | if u'适宜' in suggestion:
20 | return u'今天天气不错,空气清新,适合出门运动哦'
21 | else:
22 | return u'空气质量比较一般,建议减少出行'
23 | elif weather_code in range(10, 16):
24 | return u'主人,出门记得带伞哦'
25 | elif weather_code in range(16, 19) or \
26 | weather_code in range(25, 30) or \
27 | weather_code in range(34, 37):
28 | return u'极端天气来临,尽量待屋里陪我玩吧'
29 | elif weather_code == 38:
30 | return u'天气炎热,记得多补充水分哦'
31 | elif weather_code == 37:
32 | return u'好冷的天,记得穿厚一点哦'
33 | else:
34 | return u''
35 |
36 |
37 | def fetch_weather(api, key, location):
38 | result = requests.get(api, params={
39 | 'key': key,
40 | 'location': location
41 | }, timeout=3)
42 | res = json.loads(result.text, encoding='utf-8')
43 | return res
44 |
45 |
46 | def handle(text, mic, profile, wxbot=None):
47 | """
48 | Responds to user-input, typically speech text
49 |
50 | Arguments:
51 | text -- user-input, typically transcribed speech
52 | mic -- used to interact with the user (for both input and output)
53 | profile -- contains information related to the user (e.g., phone
54 | number)
55 | wxbot -- wechat bot instance
56 | """
57 | logger = logging.getLogger(__name__)
58 | # get config
59 | if SLUG not in profile or \
60 | 'key' not in profile[SLUG] or \
61 | (
62 | 'location' not in profile[SLUG] and
63 | 'location' not in profile
64 | ):
65 | mic.say('天气插件配置有误,插件使用失败', cache=True)
66 | return
67 | key = profile[SLUG]['key']
68 | if 'location' in profile[SLUG]:
69 | location = profile[SLUG]['location']
70 | else:
71 | location = profile['location']
72 | WEATHER_API = 'https://api.seniverse.com/v3/weather/daily.json'
73 | SUGGESTION_API = 'https://api.seniverse.com/v3/life/suggestion.json'
74 | try:
75 | weather = fetch_weather(WEATHER_API, key, location)
76 | logger.debug("Weather report: ", weather)
77 | if weather.has_key('results'):
78 | daily = weather['results'][0]['daily']
79 | days = set([])
80 | day_text = [u'今天', u'明天', u'后天']
81 | for word in day_text:
82 | if word in text:
83 | days.add(day_text.index(word))
84 | if not any(word in text for word in day_text):
85 | days = set([0, 1, 2])
86 | responds = u'%s天气:' % location
87 | analyze_res = ''
88 | for day in days:
89 | responds += u'%s:%s,%s到%s摄氏度。' % (day_text[day], daily[day]['text_day'], daily[day]['low'], daily[day]['high'])
90 | if day == 0:
91 | suggestion = fetch_weather(SUGGESTION_API, key, location)
92 | if suggestion.has_key('results'):
93 | suggestion_text = suggestion['results'][0]['suggestion']['sport']['brief']
94 | analyze_res = analyze_today(daily[day]['code_day'], suggestion_text)
95 | responds += analyze_res
96 | mic.say(responds, cache=True)
97 | else:
98 | mic.say('抱歉,我获取不到天气数据,请稍后再试', cache=True)
99 | except Exception, e:
100 | logger.error(e)
101 | mic.say('抱歉,我获取不到天气数据,请稍后再试', cache=True)
102 |
103 |
104 | def isValid(text):
105 | """
106 | Returns True if the input is related to weather.
107 |
108 | Arguments:
109 | text -- user-input, typically transcribed speech
110 | """
111 | return any(word in text for word in [u"天气", u"气温"])
112 |
--------------------------------------------------------------------------------
/WebServer.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | # HTTP服务器插件
3 | import logging
4 | import sys
5 | import os
6 | import time
7 | import subprocess
8 |
9 | reload(sys)
10 | sys.setdefaultencoding('utf8')
11 |
12 | WORDS = ["LIULAN"]
13 | SLUG = "webserver"
14 |
15 | def handle(text, mic, profile, wxbot=None):
16 | logger = logging.getLogger(__name__)
17 | if 'wechat' not in profile or not profile['wechat'] or wxbot is None:
18 | mic.say(u'请先在配置文件中开启微信接入功能', cache=True)
19 | return
20 | sys.path.append(mic.dingdangpath.LIB_PATH)
21 | dest_file = os.path.join(mic.dingdangpath.LOGIN_PATH, 'wxqr.png')
22 | wxbot.get_uuid()
23 | wxbot.gen_qr_code(dest_file)
24 | webport = "8080"
25 | if SLUG in profile:
26 | if 'webport' in profile[SLUG]:
27 | webport = profile[SLUG]['webport']
28 | # start server
29 | cmd = 'cd %s && python -m SimpleHTTPServer %s' % (mic.dingdangpath.LOGIN_PATH, webport)
30 | try:
31 | mic.say('正在启动服务器', cache=True)
32 | subprocess.Popen(cmd, shell=True)
33 | time.sleep(3)
34 | success = u'后台服务器启动成功,服务端口:%s' % (webport)
35 | mic.say(success, cache=True)
36 | except Exception, e:
37 | logger.error(e)
38 | mic.say('抱歉,后台服务器启动失败', cache=True)
39 |
40 | def isValid(text):
41 | return any(word in text for word in [u"浏览", u"服务器"])
42 |
--------------------------------------------------------------------------------
/WeiboHot.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf8 -*-
2 | # 输入:微博热门
3 | from __future__ import unicode_literals
4 | import re
5 | import requests
6 | import sys
7 | from bs4 import BeautifulSoup
8 | reload(sys)
9 | sys.setdefaultencoding('utf8')
10 | WORDS = ["WEIBORESOU"]
11 | SLUG = "weibo_resou"
12 |
13 | user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
14 | headers = {'User-Agent': user_agent}
15 |
16 | index_dic = {'一': 1, '二': 2, '三': 3, '四': 4, '五': 5,
17 | '六': 6, '七': 7, '八': 8, '九': 9, '十': 10,
18 | '十一': 11, '十二': 12, '十三': 13, '十四': 14, '十五': 15,
19 | '十六': 16, '十七': 17, '十八': 18, '十九': 19, '二十': 20,
20 | '二十一': 21, '二十二': 22, '二十三': 23, '二十四': 24, '二十五': 25,
21 | '二十六': 26, '二十七': 27, '二十八': 28, '二十九': 29, '三十': 30,
22 | '三十一': 31, '三十二': 32, '三十三': 33, '三十四': 34, '三十五': 35,
23 | '三十六': 36, '三十七': 37, '三十八': 38, '三十九': 39, '四十': 40,
24 | '四十一': 41, '四十二': 42, '四十三': 43, '四十四': 44, '四十五': 45,
25 | '四十六': 46, '四十七': 47, '四十八': 48, '四十九': 49, '五十': 50
26 | }
27 |
28 |
29 | def handle(text, mic, profile, wxbot=None):
30 | if SLUG not in profile:
31 | mic.say(u'微博热搜插件未启用')
32 | return
33 | else:
34 | pass
35 | try:
36 | list_50, list_50_name, list_href2 = return_resou_realtime()
37 | except:
38 | mic.say('奇怪的事情发生了', cache=True)
39 | return
40 | text_utf8 = text.decode('utf8')
41 | PATTERN = u'(微博热门|微博热搜|微博热点)([\u4e00-\u6760]{0,3}[\d]{0,3})(条*)'
42 | pattern = re.compile(PATTERN)
43 | m = pattern.search(text_utf8)
44 | if len(m.group(2)) == 0 or (int(m.group(2))) > 0:
45 | if len(m.group(2)) == 0:
46 | num_mic = 10
47 | num = 10
48 | elif (int(m.group(2))) > 0 and (int(m.group(2))) < 21:
49 | num_mic = int(m.group(2))
50 | num = num_mic
51 | else:
52 | if wxbot:
53 | num_mic = 20
54 | mic.say(u'条数过多,只播放二十条,热搜将发到微信')
55 | if (int(m.group(2))) < len(list_50_name):
56 | num = int(m.group(2))
57 | else:
58 | num = len(list_50_name)
59 | else:
60 | if (int(m.group(2))) < len(list_50_name):
61 | num_mic = int(m.group(2))
62 | else:
63 | num_mic = len(list_50_name)
64 |
65 | if wxbot:
66 | list_send = list_50[0:num]
67 | list_send = str(list_send).replace('u\'', '\'')
68 | list_send = list_send.decode("unicode-escape")
69 | list_send = list_send.encode('utf-8')
70 | wxbot.send_msg_by_uid(list_send, 'filehelper')
71 | else:
72 | pass
73 | for i in range(num_mic):
74 | mic.say(list_50_name[i])
75 | mic.say(u'您对第几条感兴趣?', cache=True)
76 | interest = mic.activeListen(MUSIC=True)
77 | if not interest or len(interest) == 0:
78 | mic.say(u'没有收到指令,已结束', cache=True)
79 | return
80 | else:
81 | try:
82 | PATTERN_in = u'(第)([\u4e00-\u6760]{0,3}[\d]{0,3})(条*)'
83 | pattern_in = re.compile(PATTERN_in)
84 | m_in = pattern_in.search(interest)
85 | if isinstance(m_in.group(2), unicode):
86 | pass
87 | else:
88 | int(m_in.group(2))
89 | except:
90 | mic.say(u'指令有错误', cache=True)
91 | return
92 | try:
93 | if(int(m_in.group(2))) > 0 and (int(m_in.group(2))) < num_mic + 1:
94 | index = int(m_in.group(2))
95 | interest = get_interest(list_href2[index - 1])
96 | interest = interest.encode('utf-8')
97 | mic.say(interest)
98 | else:
99 | mic.say(u'不存在这个序号的微博')
100 | except:
101 | if m_in.group(2) in index_dic:
102 | index = index_dic[m_in.group(2)]
103 | if index < len(list_href2):
104 | interest = get_interest(list_href2[index - 1])
105 | interest = interest.encode('utf-8')
106 | mic.say(interest)
107 | else:
108 | mic.say(u'不存在这个序号的微博', cache=True)
109 | else:
110 | mic.say(u'不存在这个序号的微博', cache=True)
111 | else:
112 | mic.say(u'指令有错误', cache=True)
113 |
114 |
115 | def get_interest(url):
116 | url_get = requests.get(url, headers=headers)
117 | soup = BeautifulSoup(url_get.text, "html5lib")
118 | hot = soup.find_all('p', class_='comment_txt')
119 | x = (hot[0])
120 | x = str(x)
121 | rm_label = re.compile(r'<[^>]+>')
122 | rm_result = rm_label.sub('', x)
123 | rm_result = rm_result.replace('@', '').replace(
124 | '\n', '').replace('\r', '').replace('#', '')
125 | return rm_result
126 |
127 |
128 | def resou(url):
129 | try:
130 | try:
131 | url_get = requests.get(url, headers=headers)
132 | except:
133 | print("微博热搜模块提醒您请检查网络或者微博服务器")
134 | return 0, 0, 0
135 | soup = BeautifulSoup(url_get.text, "html5lib")
136 | # 获取热搜名称
137 | # 获取热搜关注数
138 | # 获取热搜地址
139 | list_name = []
140 | list_num = []
141 | list_href = []
142 | for tag_name in soup.find_all(href=re.compile("Refer=top"), target="_blank"):
143 | if tag_name.string is not None:
144 | list_name.append(tag_name.string)
145 | for tag_num in soup.find_all(class_="star_num"):
146 | if tag_num.string is not None:
147 | list_num.append(tag_num.string)
148 | for tag_name in soup.find_all(href=re.compile("Refer=top"), target="_blank"):
149 | if tag_name['href'] is not None:
150 | tag_name_ = 'http://s.weibo.com' + tag_name['href']
151 | list_href.append(tag_name_)
152 | return list_name, list_num, list_href
153 | except:
154 | return 1, 1, 1
155 |
156 |
157 | '''
158 | #微博热搜前十
159 | def return_resou_homepage():
160 |
161 | list_name1, list_num1, list_href1=resou(
162 | 'http://s.weibo.com/top/summary?cate=homepage')
163 | return list_name1, list_num1, list_href1
164 |
165 | '''
166 |
167 |
168 | def return_resou_realtime():
169 | list_name2, list_num2, list_href2 = resou(
170 | 'http://s.weibo.com/top/summary?cate=realtimehot')
171 | if list_num2 == 0 or list_num2 == 1:
172 | return 0
173 | else:
174 | list_href2 = list_href2[::2]
175 | list_50 = range(len(list_name2))
176 | list_50_name = range(len(list_name2))
177 | if(len(list_name2)) == 49:
178 | del list_href2[2]
179 | for i in range(len(list_name2)):
180 | list_50[i] = '第' + str(i + 1) + '条' + ' ' + '热搜值: ' + str(
181 | list_num2[i]) + ' ' + list_name2[i] + ' ' + list_href2[i] + '\n '
182 | list_50_name[i] = '第' + \
183 | str(i + 1) + '条' + '.' + list_name2[i] + '\n'
184 | return list_50, list_50_name, list_href2
185 |
186 |
187 | def isValid(text):
188 | return any(word in text for word in [u"微博热搜", u"微博热点", u"微博热门"])
189 |
190 |
191 | if __name__ == '__main__':
192 |
193 | try:
194 | a, b, c = return_resou_realtime()
195 | for x in a:
196 | print(x)
197 | except:
198 | print("奇怪的事情发生了") # d, e, f=return_resou_homepage()
199 |
--------------------------------------------------------------------------------
/YouDaoFanYi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8
2 | # 有道翻译插件
3 |
4 | import logging
5 | import json
6 | import urllib
7 | import time
8 | import re
9 | import requests
10 | import md5
11 | import random
12 | import sys
13 |
14 | reload(sys)
15 | sys.setdefaultencoding('utf8')
16 |
17 | SLUG = "youdao"
18 | WORDS = ["FANYI"]
19 |
20 | def translate(appId, appSecret, sentence):
21 | logger = logging.getLogger(__name__)
22 | url = 'https://openapi.youdao.com/api'
23 | salt = random.randint(1, 65536)
24 | sign = appId+sentence+str(salt)+appSecret
25 | m1 = md5.new()
26 | m1.update(sign)
27 | sign = m1.hexdigest()
28 | params = {
29 | 'q': sentence,
30 | 'from': 'auto',
31 | 'to': 'auto',
32 | 'appKey': appId,
33 | 'salt': salt,
34 | 'sign': sign
35 | }
36 | result = requests.get(url, params=params)
37 | res = json.loads(result.text, encoding='utf-8')
38 | s = res['translation'][0]
39 | return s
40 |
41 |
42 | def getSentence(text):
43 | pattern1 = re.compile("翻译.*?")
44 | pattern2 = re.compile(".*?的翻译")
45 |
46 | if re.match(pattern1, text) != None:
47 | sentence = text.replace("翻译", "")
48 | elif re.match(pattern2, text) != None:
49 | sentence = text.replace("的翻译", "")
50 | else:
51 | sentence = ""
52 | sentence = sentence.replace(",","")
53 | sentence = sentence.replace(",","")
54 | return sentence
55 |
56 |
57 | def handle(text, mic, profile, wxbot=None):
58 | logger = logging.getLogger(__name__)
59 | if SLUG not in profile or \
60 | 'appId' not in profile[SLUG] or\
61 | 'appSecret' not in profile[SLUG]:
62 | mic.say('有道翻译插件配置有误,插件使用失败', cache=True)
63 | return
64 | appId = profile[SLUG]['appId']
65 | appSecret = profile[SLUG]['appSecret']
66 | sentence = getSentence(text)
67 | logger.info('sentence: ' + sentence)
68 | if sentence:
69 | try:
70 | s = translate(appId, appSecret, sentence)
71 | if s:
72 | mic.say(sentence+"的翻译是" + s, cache=False)
73 | else:
74 | mic.say("翻译" + sentence +"失败,请稍后再试", cache=False)
75 | except Exception, e:
76 | logger.error(e)
77 | mic.say('抱歉, 我不知道怎么翻译' + sentence, cache=False)
78 | else:
79 | mic.say(u"没有听清楚 请重试", cache=True)
80 |
81 |
82 | def isValid(text):
83 | return u"翻译" in text
84 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | MusicBoxApi>=1.0.0
2 | paho-mqtt>=1.3.0
3 | eyeD3>=0.8
4 | wiringpi>=2.46.0
5 |
--------------------------------------------------------------------------------
/xiaoice.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8-*-
2 | #微软小冰聊天插件
3 |
4 | import requests
5 | import sys
6 | import time
7 | import json
8 | import os
9 | reload(sys)
10 | sys.setdefaultencoding('utf8')
11 | SLUG = "XIAOICE"
12 | WORDS = ["XIAOICE"]
13 | def handle(text, mic, profile, wxbot=None):
14 | """
15 | Responds to user-input, typically speech text
16 | Arguments:
17 | text -- user-input, typically transcribed speech
18 | mic -- used to interact with the user (for both input and output)
19 | profile -- contains information related to the user (e.g., phone
20 | number)
21 | wxbot -- wechat bot instance
22 | """
23 | if SLUG not in profile or \
24 | 'get_cookie' not in profile[SLUG] or \
25 | 'send_cookie' not in profile[SLUG]:
26 | mic.say('小冰配置错误,暂无法使用', cache=True)
27 | return
28 | get_cookie = profile[SLUG]['get_cookie']
29 | send_cookie = profile[SLUG]['send_cookie']
30 |
31 |
32 | if any(word in text for word in [u"召唤女神", u"找女神聊天"]):
33 | mic.say(u"我是人见人爱的小冰,快来和我聊聊天吧", cache=True)
34 | mic.chatting_mode = True
35 | mic.skip_passive = True
36 | elif any(word in text for word in [u"不要女神了", u"再见女神"]):
37 | mic.say(u"轻轻的我走了,正如我轻轻地来。我们下次再聊吧", cache=True)
38 | mic.skip_passive = False
39 | mic.chatting_mode = False
40 | else:
41 | message = text.replace('女神','').replace(',','')
42 | xiaoice = MS_xiaoice(get_cookie,send_cookie)
43 | xiaoice.send_message(message)
44 | txt = xiaoice.get_message()
45 | if txt:
46 | mic.say(txt, cache=True)
47 |
48 | def isValid(text):
49 | """
50 | Returns True if the input is related to weather.
51 | Arguments:
52 | text -- user-input, typically transcribed speech
53 | """
54 | return any(word in text for word in [u"女神"])
55 |
56 |
57 |
58 | class MS_xiaoice(object):
59 | def __init__(self, get_cookie, send_cookie):
60 | self.text = ''
61 | self.now = str(int(time.time()))
62 | self.get_cookie = get_cookie
63 | self.send_cookie = send_cookie
64 |
65 | def send_message(self,text):
66 | if text != '':
67 | self.text = text
68 | else:
69 | return
70 | send_url = 'https://weibo.com/aj/message/add?ajwvr=6&__rnd=' + self.now
71 | headers = {
72 | 'Referer': 'https://weibo.com/messages?topnav=1&wvr=6',
73 | 'cookie': self.send_cookie
74 | }
75 | data = {
76 | 'location': 'msgdialog',
77 | 'module': 'msgissue',
78 | 'style_id': '1',
79 | 'text': self.text,
80 | 'uid': '5175429989',
81 | 'tovfids': '',
82 | 'fids': '',
83 | 'el': '[object HTMLDivElement]',
84 | '_t': '0'
85 | }
86 | try:
87 | sendMessage = requests.post(send_url, data=data, headers=headers)
88 | time.sleep(1)
89 | print '消息发送成功'
90 | except:
91 | print '消息发送失败'
92 |
93 | def get_message(self):
94 | get_url = 'https://m.weibo.cn/msg/messages?uid=5175429989&page=1'
95 | header = {
96 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) \
97 | Chrome/60.0.3112.113 Safari/537.36',
98 | 'Referer': 'https://login.sina.com.cn/crossdomain2.php?action=login&entry=sinawap&r=\
99 | https%3A%2F%2Fpassport.weibo.cn%2Fsso%2Fcrossdomain%3Fservice%3Dsinawap%26display%3D0%26ssosavestate%3D153\
100 | 6888171%26url%3Dhttps%253A%252F%252Fm.weibo.cn%252Fmsg%252Fmessages%253Fuid%253D5175429989%2526page%2\
101 | 53D1%26ticket%3DST-NTM0MzE0MjMzMA%3D%3D-1505358289-tc-EA58DC48B5C15C0D82E8C7B93C38C651-1%26retcode%3D0',
102 | 'cookie': self.get_cookie
103 | }
104 | getMessage = json.loads(requests.get(get_url, headers=header,verify=False).text)
105 | time.sleep(1)
106 | while getMessage['data'][0]['text'] == unicode(self.text,'utf-8'):
107 | getMessage = json.loads(requests.get(get_url, headers=header,verify=False).text)
108 | if getMessage['data'][0]['text'] == u'分享语音':
109 | tts_url = 'http://upload.api.weibo.com/2/mss/msget?source=351354573&fid=' \
110 | + str(getMessage['data'][0]['attachment'][0]['fid'])
111 | r = requests.get(tts_url, headers=header)
112 | with open('/tmp/tts_voice.mp3', 'wb') as f:
113 | f.write(r.content)
114 | os.system('mplayer /tmp/tts_voice.mp3')
115 | return
116 | elif getMessage['data'][1]['text'] != unicode(self.text,'utf-8'):
117 | return getMessage['data'][1]['text'] + getMessage['data'][0]['text']
118 | else:
119 | return getMessage['data'][0]['text']
120 |
--------------------------------------------------------------------------------