├── .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 | --------------------------------------------------------------------------------