events.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. # -*- coding: utf-8 -*-
  2. """
  3. wechatpy.events
  4. ~~~~~~~~~~~~~~~~
  5. This module contains all the events WeChat callback uses.
  6. :copyright: (c) 2014 by messense.
  7. :license: MIT, see LICENSE for more details.
  8. """
  9. from __future__ import absolute_import, unicode_literals
  10. from library.wechatpy.fields import (
  11. StringField,
  12. FloatField,
  13. IntegerField,
  14. BaseField,
  15. Base64DecodeField,
  16. DateTimeField
  17. )
  18. from library.wechatpy.messages import BaseMessage
  19. EVENT_TYPES = {}
  20. def register_event(event_type):
  21. """
  22. Register the event class so that they can be accessed from EVENT_TYPES
  23. :param event_type: Event type
  24. """
  25. def register(cls):
  26. EVENT_TYPES[event_type] = cls
  27. return cls
  28. return register
  29. class BaseEvent(BaseMessage):
  30. """Base class for all events"""
  31. type = 'event'
  32. event = ''
  33. @register_event('subscribe')
  34. class SubscribeEvent(BaseEvent):
  35. """
  36. 用户关注事件
  37. 详情请参阅
  38. https://mp.weixin.qq.com/wiki?id=mp1421140454
  39. """
  40. event = 'subscribe'
  41. key = StringField('EventKey', '')
  42. @register_event('unsubscribe')
  43. class UnsubscribeEvent(BaseEvent):
  44. """
  45. 用户取消关注事件
  46. 详情请参阅
  47. https://mp.weixin.qq.com/wiki?id=mp1421140454
  48. """
  49. event = 'unsubscribe'
  50. @register_event('subscribe_scan')
  51. class SubscribeScanEvent(BaseEvent):
  52. """
  53. 用户扫描二维码关注事件
  54. 详情请参阅
  55. https://mp.weixin.qq.com/wiki?id=mp1421140454
  56. """
  57. event = 'subscribe_scan'
  58. scene_id = StringField('EventKey')
  59. ticket = StringField('Ticket')
  60. @register_event('scan')
  61. class ScanEvent(BaseEvent):
  62. """
  63. 用户扫描二维码事件
  64. 详情请参阅
  65. https://mp.weixin.qq.com/wiki?id=mp1421140454
  66. """
  67. event = 'scan'
  68. scene_id = StringField('EventKey')
  69. ticket = StringField('Ticket')
  70. @register_event('location')
  71. class LocationEvent(BaseEvent):
  72. """
  73. 上报地理位置事件
  74. 详情请参阅
  75. https://mp.weixin.qq.com/wiki?id=mp1421140454
  76. """
  77. event = 'location'
  78. latitude = FloatField('Latitude', 0.0)
  79. longitude = FloatField('Longitude', 0.0)
  80. precision = FloatField('Precision', 0.0)
  81. @register_event('click')
  82. class ClickEvent(BaseEvent):
  83. """
  84. 点击菜单拉取消息事件
  85. 详情请参阅
  86. https://mp.weixin.qq.com/wiki?id=mp1421140454
  87. """
  88. event = 'click'
  89. key = StringField('EventKey')
  90. @register_event('view')
  91. class ViewEvent(BaseEvent):
  92. """
  93. 点击菜单跳转链接事件
  94. 详情请参阅
  95. https://mp.weixin.qq.com/wiki?id=mp1421140454
  96. """
  97. event = 'view'
  98. url = StringField('EventKey')
  99. @register_event('masssendjobfinish')
  100. class MassSendJobFinishEvent(BaseEvent):
  101. """
  102. 群发消息任务完成事件
  103. 详情请参阅
  104. https://mp.weixin.qq.com/wiki?id=mp1481187827_i0l21
  105. """
  106. id = IntegerField('MsgID', 0)
  107. event = 'masssendjobfinish'
  108. status = StringField('Status')
  109. total_count = IntegerField('TotalCount', 0)
  110. filter_count = IntegerField('FilterCount', 0)
  111. sent_count = IntegerField('SentCount', 0)
  112. error_count = IntegerField('ErrorCount', 0)
  113. @register_event('templatesendjobfinish')
  114. class TemplateSendJobFinishEvent(BaseEvent):
  115. """
  116. 模板消息任务完成事件
  117. 详情请参阅
  118. https://mp.weixin.qq.com/wiki?id=mp1433751277
  119. """
  120. id = IntegerField('MsgID')
  121. event = 'templatesendjobfinish'
  122. status = StringField('Status')
  123. class BaseScanCodeEvent(BaseEvent):
  124. key = StringField('EventKey')
  125. scan_code_info = BaseField('ScanCodeInfo', {})
  126. @property
  127. def scan_type(self):
  128. return self.scan_code_info['ScanType']
  129. @property
  130. def scan_result(self):
  131. return self.scan_code_info['ScanResult']
  132. @register_event('scancode_push')
  133. class ScanCodePushEvent(BaseScanCodeEvent):
  134. """
  135. 扫码推事件
  136. 详情请参阅
  137. https://mp.weixin.qq.com/wiki?id=mp1421141016
  138. """
  139. event = 'scancode_push'
  140. @register_event('scancode_waitmsg')
  141. class ScanCodeWaitMsgEvent(BaseScanCodeEvent):
  142. """
  143. 扫码推事件且弹出“消息接收中”提示框的事件
  144. 详情请参阅
  145. https://mp.weixin.qq.com/wiki?id=mp1421141016
  146. """
  147. event = 'scancode_waitmsg'
  148. class BasePictureEvent(BaseEvent):
  149. key = StringField('EventKey')
  150. pictures_info = BaseField('SendPicsInfo', {})
  151. @property
  152. def count(self):
  153. return int(self.pictures_info['Count'])
  154. @property
  155. def pictures(self):
  156. if self.pictures_info['PicList']:
  157. items = self.pictures_info['PicList']['item']
  158. if self.count > 1:
  159. return items
  160. return [items]
  161. return []
  162. @register_event('pic_sysphoto')
  163. class PicSysPhotoEvent(BasePictureEvent):
  164. """
  165. 弹出系统拍照发图的事件
  166. 详情请参阅
  167. https://mp.weixin.qq.com/wiki?id=mp1421141016
  168. """
  169. event = 'pic_sysphoto'
  170. @register_event('pic_photo_or_album')
  171. class PicPhotoOrAlbumEvent(BasePictureEvent):
  172. """
  173. 弹出拍照或者相册发图的事件
  174. 详情请参阅
  175. https://mp.weixin.qq.com/wiki?id=mp1421141016
  176. """
  177. event = 'pic_photo_or_album'
  178. @register_event('pic_weixin')
  179. class PicWeChatEvent(BasePictureEvent):
  180. """
  181. 弹出微信相册发图器的事件
  182. 详情请参阅
  183. https://mp.weixin.qq.com/wiki?id=mp1421141016
  184. """
  185. event = 'pic_weixin'
  186. @register_event('location_select')
  187. class LocationSelectEvent(BaseEvent):
  188. """
  189. 弹出地理位置选择器的事件
  190. 详情请参阅
  191. https://mp.weixin.qq.com/wiki?id=mp1421141016
  192. """
  193. event = 'location_select'
  194. key = StringField('EventKey')
  195. location_info = BaseField('SendLocationInfo', {})
  196. @property
  197. def location_x(self):
  198. return self.location_info['Location_X']
  199. @property
  200. def location_y(self):
  201. return self.location_info['Location_Y']
  202. @property
  203. def location(self):
  204. return self.location_x, self.location_y
  205. @property
  206. def scale(self):
  207. return self.location_info['Scale']
  208. @property
  209. def label(self):
  210. return self.location_info['Label']
  211. @property
  212. def poiname(self):
  213. return self.location_info['Poiname']
  214. @register_event('card_pass_check')
  215. class CardPassCheckEvent(BaseEvent):
  216. event = 'card_pass_check'
  217. card_id = StringField('CardId')
  218. @register_event('card_not_pass_check')
  219. class CardNotPassCheckEvent(BaseEvent):
  220. event = 'card_not_pass_check'
  221. card_id = StringField('CardId')
  222. @register_event('user_get_card')
  223. class UserGetCardEvent(BaseEvent):
  224. """
  225. 领取事件推送
  226. 详情请参阅
  227. https://mp.weixin.qq.com/wiki?id=mp1451025274
  228. """
  229. event = 'user_get_card'
  230. card_id = StringField('CardId')
  231. is_given_by_friend = IntegerField('IsGiveByFriend')
  232. friend = StringField('FriendUserName')
  233. code = StringField('UserCardCode')
  234. old_code = StringField('OldUserCardCode')
  235. outer_id = StringField('OuterId')
  236. @register_event('user_del_card')
  237. class UserDeleteCardEvent(BaseEvent):
  238. """
  239. 卡券删除事件推送
  240. 详情请参阅
  241. https://mp.weixin.qq.com/wiki?id=mp1451025274
  242. """
  243. event = 'user_del_card'
  244. card_id = StringField('CardId')
  245. code = StringField('UserCardCode')
  246. @register_event('user_consume_card')
  247. class UserConsumeCardEvent(BaseEvent):
  248. """
  249. 卡券核销事件推送
  250. 详情请参阅
  251. https://mp.weixin.qq.com/wiki?id=mp1451025274
  252. """
  253. event = 'user_consume_card'
  254. card_id = StringField('CardId')
  255. code = StringField('UserCardCode')
  256. consume_source = StringField('ConsumeSource')
  257. location_id = StringField('LocationId')
  258. staff = StringField('StaffOpenId')
  259. @register_event('merchant_order')
  260. class MerchantOrderEvent(BaseEvent):
  261. event = 'merchant_order'
  262. order_id = StringField('OrderId')
  263. order_status = IntegerField('OrderStatus')
  264. product_id = StringField('ProductId')
  265. sku_info = StringField('SkuInfo')
  266. @register_event('kf_create_session')
  267. class KfCreateSessionEvent(BaseEvent):
  268. event = 'kf_create_session'
  269. account = StringField('KfAccount')
  270. @register_event('kf_close_session')
  271. class KfCloseSessionEvent(BaseEvent):
  272. event = 'kf_close_session'
  273. account = StringField('KfAccount')
  274. @register_event('kf_switch_session')
  275. class KfSwitchSessionEvent(BaseEvent):
  276. event = 'kf_switch_session'
  277. from_account = StringField('FromKfAccount')
  278. to_account = StringField('ToKfAccount')
  279. @register_event('device_text')
  280. class DeviceTextEvent(BaseEvent):
  281. event = 'device_text'
  282. device_type = StringField('DeviceType')
  283. device_id = StringField('DeviceID')
  284. session_id = StringField('SessionID')
  285. content = Base64DecodeField('Content')
  286. open_id = StringField('OpenID')
  287. @register_event('device_bind')
  288. class DeviceBindEvent(BaseEvent):
  289. event = 'device_bind'
  290. device_type = StringField('DeviceType')
  291. device_id = StringField('DeviceID')
  292. session_id = StringField('SessionID')
  293. content = Base64DecodeField('Content')
  294. open_id = StringField('OpenID')
  295. @register_event('device_unbind')
  296. class DeviceUnbindEvent(BaseEvent):
  297. event = 'device_unbind'
  298. device_type = StringField('DeviceType')
  299. device_id = StringField('DeviceID')
  300. session_id = StringField('SessionID')
  301. content = Base64DecodeField('Content')
  302. open_id = StringField('OpenID')
  303. @register_event('device_subscribe_status')
  304. class DeviceSubscribeStatusEvent(BaseEvent):
  305. event = 'device_subscribe_status'
  306. device_type = StringField('DeviceType')
  307. device_id = StringField('DeviceID')
  308. open_id = StringField('OpenID')
  309. op_type = IntegerField('OpType')
  310. @register_event('device_unsubscribe_status')
  311. class DeviceUnsubscribeStatusEvent(BaseEvent):
  312. event = 'device_unsubscribe_status'
  313. device_type = StringField('DeviceType')
  314. device_id = StringField('DeviceID')
  315. open_id = StringField('OpenID')
  316. op_type = IntegerField('OpType')
  317. @register_event('shakearoundusershake')
  318. class ShakearoundUserShakeEvent(BaseEvent):
  319. event = 'shakearound_user_shake'
  320. _chosen_beacon = BaseField('ChosenBeacon', {})
  321. _around_beacons = BaseField('AroundBeacons', {})
  322. @property
  323. def chosen_beacon(self):
  324. beacon = self._chosen_beacon
  325. if not beacon:
  326. return {}
  327. return {
  328. 'uuid': beacon['Uuid'],
  329. 'major': beacon['Major'],
  330. 'minor': beacon['Minor'],
  331. 'distance': float(beacon['Distance']),
  332. }
  333. @property
  334. def around_beacons(self):
  335. beacons = self._around_beacons
  336. if not beacons:
  337. return []
  338. ret = []
  339. for beacon in beacons['AroundBeacon']:
  340. ret.append({
  341. 'uuid': beacon['Uuid'],
  342. 'major': beacon['Major'],
  343. 'minor': beacon['Minor'],
  344. 'distance': float(beacon['Distance']),
  345. })
  346. return ret
  347. @register_event('poi_check_notify')
  348. class PoiCheckNotifyEvent(BaseEvent):
  349. event = 'poi_check_notify'
  350. poi_id = StringField('PoiId')
  351. uniq_id = StringField('UniqId')
  352. result = StringField('Result')
  353. message = StringField('Msg')
  354. @register_event('wificonnected')
  355. class WiFiConnectedEvent(BaseEvent):
  356. event = 'wificconnected'
  357. connect_time = IntegerField('ConnectTime')
  358. expire_time = IntegerField('ExpireTime')
  359. vendor_id = StringField('VendorId')
  360. shop_id = StringField('PlaceId')
  361. bssid = StringField('DeviceNo')
  362. # ============================================================================
  363. # 微信认证事件推送
  364. # ============================================================================
  365. @register_event('qualification_verify_success')
  366. class QualificationVerifySuccessEvent(BaseEvent):
  367. """
  368. 资质认证成功事件
  369. 详情请参阅
  370. https://mp.weixin.qq.com/wiki?id=mp1455785130
  371. """
  372. event = 'qualification_verify_success'
  373. expired_time = DateTimeField('ExpiredTime')
  374. @register_event('qualification_verify_fail')
  375. class QualificationVerifyFailEvent(BaseEvent):
  376. """
  377. 资质认证失败事件
  378. 详情请参阅
  379. https://mp.weixin.qq.com/wiki?id=mp1455785130
  380. """
  381. event = 'qualification_verify_fail'
  382. fail_time = DateTimeField('FailTime')
  383. fail_reason = StringField('FailReason')
  384. @register_event('naming_verify_success')
  385. class NamingVerifySuccessEvent(BaseEvent):
  386. """
  387. 名称认证成功事件
  388. 详情请参阅
  389. https://mp.weixin.qq.com/wiki?id=mp1455785130
  390. """
  391. event = 'naming_verify_success'
  392. expired_time = DateTimeField('ExpiredTime')
  393. @register_event('naming_verify_fail')
  394. class NamingVerifyFailEvent(BaseEvent):
  395. """
  396. 名称认证失败事件
  397. 客户端不打勾,但仍有接口权限。详情请参阅
  398. https://mp.weixin.qq.com/wiki?id=mp1455785130
  399. """
  400. event = 'naming_verify_fail'
  401. fail_time = DateTimeField('FailTime')
  402. fail_reason = StringField('FailReason')
  403. @register_event('annual_renew')
  404. class AnnualRenewEvent(BaseEvent):
  405. """
  406. 年审通知事件
  407. 详情请参阅
  408. https://mp.weixin.qq.com/wiki?id=mp1455785130
  409. """
  410. event = 'annual_renew'
  411. expired_time = DateTimeField('ExpiredTime')
  412. @register_event('verify_expired')
  413. class VerifyExpiredEvent(BaseEvent):
  414. """
  415. 认证过期失效通知
  416. 详情请参阅
  417. https://mp.weixin.qq.com/wiki?id=mp1455785130
  418. """
  419. event = 'verify_expired'
  420. expired_time = DateTimeField('ExpiredTime')
  421. @register_event('user_scan_product')
  422. class UserScanProductEvent(BaseEvent):
  423. """
  424. 打开商品主页事件
  425. 详情请参考
  426. https://mp.weixin.qq.com/wiki?id=mp1455872179
  427. """
  428. event = 'user_scan_product'
  429. standard = StringField('KeyStandard')
  430. key = StringField('KeyStr')
  431. country = StringField('Country')
  432. province = StringField('Province')
  433. city = StringField('City')
  434. sex = IntegerField('Sex')
  435. scene = IntegerField('Scene')
  436. @register_event('user_scan_product_enter_session')
  437. class UserScanProductEnterSessionEvent(BaseEvent):
  438. """
  439. 进入公众号事件
  440. 详情请参考
  441. https://mp.weixin.qq.com/wiki?id=mp1455872179
  442. """
  443. event = 'user_scan_product_enter_session'
  444. standard = StringField('KeyStandard')
  445. key = StringField('KeyStr')
  446. @register_event('user_scan_product_async')
  447. class UserScanProductAsyncEvent(BaseEvent):
  448. """
  449. 地理位置信息异步推送事件
  450. 详情请参考
  451. https://mp.weixin.qq.com/wiki?id=mp1455872179
  452. """
  453. event = 'user_scan_product_async'
  454. standard = StringField('KeyStandard')
  455. key = StringField('KeyStr')
  456. region_code = StringField('RegionCode')
  457. @register_event('user_scan_product_verify_action')
  458. class UserScanProductVerifyActionEvent(BaseEvent):
  459. """
  460. 商品审核结果事件
  461. 详情请参考
  462. https://mp.weixin.qq.com/wiki?id=mp1455872179
  463. """
  464. event = 'user_scan_product_verify_action'
  465. standard = StringField('KeyStandard')
  466. key = StringField('KeyStr')
  467. result = StringField('Result')
  468. reason = StringField('ReasonMsg')
  469. @register_event('subscribe_scan_product')
  470. class SubscribeScanProductEvent(BaseEvent):
  471. """
  472. 用户在商品主页中关注公众号事件
  473. 详情请参考
  474. https://mp.weixin.qq.com/wiki?id=mp1455872179
  475. """
  476. event = 'subscribe_scan_product'
  477. event_key = StringField('EventKey')
  478. @property
  479. def scene(self):
  480. return self.event_key.split('|', 1)[0]
  481. @property
  482. def standard(self):
  483. return self.event_key.split('|')[1]
  484. @property
  485. def key(self):
  486. return self.event_key.split('|')[2]
  487. @register_event('user_authorize_invoice')
  488. class UserAuthorizeInvoiceEvent(BaseEvent):
  489. """
  490. 用户授权发票事件
  491. (会包含一个订单号,不成功就失败)
  492. 详情请参考
  493. https://mp.weixin.qq.com/wiki?id=mp1497082828_r1cI2
  494. """
  495. event = 'user_authorize_invoice'
  496. success_order_id = StringField('SuccOrderId') # 授权成功的订单号
  497. fail_order_id = StringField('FailOrderId') # 授权失败的订单号
  498. app_id = StringField('AppId') # 用于接收事件推送的公众号的AppId
  499. auth_source = StringField('Source') # 授权来源,web表示来自微信内H5,app标识来自app
  500. @register_event('update_invoice_status')
  501. class UpdateInvoiceStatusEvent(BaseEvent):
  502. """
  503. 发票状态更新事件
  504. 详情请参考
  505. https://mp.weixin.qq.com/wiki?id=mp1497082828_r1cI2
  506. """
  507. event = 'update_invoice_status'
  508. status = StringField('Status') # 发票报销状态
  509. card_id = StringField('CardId') # 发票 Card ID
  510. code = StringField('Code') # 发票 Code
  511. @register_event('submit_invoice_title')
  512. class SubmitInvoiceTitleEvent(BaseEvent):
  513. """
  514. 用户提交发票抬头事件
  515. 详情请参考
  516. https://mp.weixin.qq.com/wiki?id=mp1496554912_vfWU0
  517. """
  518. event = 'submit_invoice_title'
  519. title = StringField('title') # 抬头
  520. phone = StringField('phone') # 联系方式
  521. tax_no = StringField('tax_no') # 税号
  522. addr = StringField('addr') # 地址
  523. bank_type = StringField('bank_type') # 银行类型
  524. bank_no = StringField('bank_no') # 银行号码
  525. attach = StringField('attach') # 附加字段
  526. title_type = StringField('title_type') # 抬头类型,个人InvoiceUserTitlePersonType, 公司InvoiceUserTitleBusinessType
  527. @register_event('user_enter_tempsession')
  528. class UserEnterTempSessionEvent(BaseEvent):
  529. """
  530. 小程序用户进入客服消息
  531. 详情请参阅
  532. https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/customer-message/receive.html
  533. """
  534. event = 'user_enter_tempsession'
  535. session_from = StringField('SessionFrom')
  536. @register_event('view_miniprogram')
  537. class ViewMiniProgramEvent(BaseEvent):
  538. """
  539. 从菜单进入小程序事件
  540. """
  541. event = 'view_miniprogram'
  542. page_path = StringField('EventKey') # 小程序路径
  543. menu_id = StringField('MenuId') # 菜单ID