xiaomi-push 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +13 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +0 -1
- data/README.md +180 -44
- data/Rakefile +57 -26
- data/bin/xmp +1 -1
- data/lib/xiaomi/push.rb +6 -3
- data/lib/xiaomi/push/client.rb +70 -16
- data/lib/xiaomi/push/commands.rb +1 -0
- data/lib/xiaomi/push/commands/feedback.rb +4 -5
- data/lib/xiaomi/push/commands/message.rb +21 -12
- data/lib/xiaomi/push/commands/user.rb +69 -0
- data/lib/xiaomi/push/const.rb +20 -2
- data/lib/xiaomi/push/platforms/android.rb +17 -0
- data/lib/xiaomi/push/platforms/ios.rb +20 -0
- data/lib/xiaomi/push/services/feedback.rb +9 -2
- data/lib/xiaomi/push/services/job.rb +36 -0
- data/lib/xiaomi/push/services/message.rb +49 -56
- data/lib/xiaomi/push/services/messages.rb +91 -0
- data/lib/xiaomi/push/services/messages/android.rb +11 -1
- data/lib/xiaomi/push/services/messages/base.rb +108 -6
- data/lib/xiaomi/push/services/messages/ios.rb +21 -1
- data/lib/xiaomi/push/services/topic.rb +39 -2
- data/lib/xiaomi/push/services/user.rb +60 -0
- data/lib/xiaomi/push/version.rb +1 -1
- data/xiaomi-push.gemspec +8 -8
- metadata +27 -51
- data/.travis.yml +0 -3
- data/lib/xiaomi/push/devices/android.rb +0 -6
- data/lib/xiaomi/push/devices/ios.rb +0 -9
- data/lib/xiaomi/push/services/multi_messages.rb +0 -0
data/bin/xmp
CHANGED
@@ -6,7 +6,7 @@ require 'bundler/setup'
|
|
6
6
|
require 'xiaomi/push'
|
7
7
|
|
8
8
|
program :version, Xiaomi::Push::VERSION
|
9
|
-
program :description, '
|
9
|
+
program :description, '小米推送命令行工具'
|
10
10
|
|
11
11
|
program :help, 'Author', 'icyleaf'
|
12
12
|
program :help, 'Website', 'icyleaf.cn@gmail.com'
|
data/lib/xiaomi/push.rb
CHANGED
@@ -3,9 +3,12 @@ require 'xiaomi/push/error'
|
|
3
3
|
require 'xiaomi/push/const'
|
4
4
|
require 'xiaomi/push/client'
|
5
5
|
|
6
|
-
require 'xiaomi/push/
|
7
|
-
require 'xiaomi/push/
|
6
|
+
require 'xiaomi/push/platforms/ios'
|
7
|
+
require 'xiaomi/push/platforms/android'
|
8
8
|
|
9
9
|
require 'xiaomi/push/services/message'
|
10
|
+
require 'xiaomi/push/services/messages'
|
10
11
|
require 'xiaomi/push/services/topic'
|
11
|
-
require 'xiaomi/push/services/
|
12
|
+
require 'xiaomi/push/services/user'
|
13
|
+
require 'xiaomi/push/services/job'
|
14
|
+
require 'xiaomi/push/services/feedback'
|
data/lib/xiaomi/push/client.rb
CHANGED
@@ -1,42 +1,96 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'http'
|
2
|
+
require 'json'
|
3
3
|
|
4
4
|
module Xiaomi
|
5
5
|
module Push
|
6
|
+
# 小米推送内置客户端
|
7
|
+
#
|
8
|
+
# 实际情况并不会直接被使用,而是使用 iOS 或 Android 的实例化
|
9
|
+
#
|
10
|
+
# @example 实例化 iOS 推送客户端(环境使用 :sandbox)
|
11
|
+
# client = Xiaomi::Push::IOS('Fill your app secret', :sandbox)
|
12
|
+
# @example 实例化 Android 推送客户端
|
13
|
+
# client = Xiaomi::Push::Android('Fill your app secret')
|
14
|
+
#
|
15
|
+
# @see https://dev.mi.com/console/doc/detail?pId=68 小米推送服务启用指南
|
6
16
|
class Client
|
7
17
|
include Const
|
8
18
|
|
9
|
-
attr_reader :device, :
|
10
|
-
def initialize(secret)
|
11
|
-
@device = self.class.name.split('::')[-1].upcase
|
12
|
-
@secret = secret
|
19
|
+
attr_reader :device, :client
|
13
20
|
|
14
|
-
|
15
|
-
|
16
|
-
|
21
|
+
# 实例化一个新的客户端
|
22
|
+
#
|
23
|
+
# @see https://dev.mi.com/console/doc/detail?pId=68 小米推送服务启用指南
|
24
|
+
#
|
25
|
+
# @param [String] secret 小米应用的 App Secret
|
26
|
+
# @param [Symbol] env 推送环境,可用的环境有 :production/:sandbox
|
27
|
+
#
|
28
|
+
# @raise [RequestError] 必须使用 Xiaomi::Push::Android 或 Xiaomi::Push::IOS 实例化
|
29
|
+
# @raise [RequestError] Android 使用 sandbox 推送环境引发异常
|
30
|
+
def initialize(secret, env = :production)
|
31
|
+
@device = self.class.name.split('::')[-1].upcase
|
32
|
+
@client = HTTP.headers(authorization: "key=#{secret}")
|
17
33
|
|
18
|
-
|
19
|
-
'Authorization' => "key=#{@secret}"
|
20
|
-
}
|
34
|
+
determine_platform!(env)
|
21
35
|
|
22
|
-
use_production!
|
36
|
+
env == :production ? use_production! : use_sandbox!
|
23
37
|
end
|
24
38
|
|
39
|
+
# 单条消息
|
25
40
|
def message
|
26
41
|
@message ||= Services::Message.new(self)
|
27
42
|
end
|
28
43
|
|
44
|
+
# 多条消息
|
45
|
+
def messages
|
46
|
+
@messages ||= Services::Messages.new(self)
|
47
|
+
end
|
48
|
+
|
49
|
+
# 标签
|
29
50
|
def topic
|
30
51
|
@topic ||= Services::Topic.new(self)
|
31
52
|
end
|
32
53
|
|
54
|
+
# 用户查询
|
55
|
+
def user
|
56
|
+
@user ||= Services::User.new(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Feedback
|
33
60
|
def feedback
|
34
61
|
@feedback ||= Services::Feedback.new(self)
|
35
62
|
end
|
36
63
|
|
37
|
-
|
38
|
-
|
39
|
-
|
64
|
+
# 以 GET 方式的网络请求
|
65
|
+
#
|
66
|
+
# @param [String] url
|
67
|
+
# @param [Hash] params
|
68
|
+
# @return [Hash] 小米返回数据结构
|
69
|
+
def get(url, params = nil)
|
70
|
+
r = @client.get(url, params: params)
|
71
|
+
data = JSON.parse(r)
|
72
|
+
end
|
73
|
+
|
74
|
+
# 以 POST 方式的网络请求
|
75
|
+
#
|
76
|
+
# @param [String] url
|
77
|
+
# @param [Hash] params
|
78
|
+
# @return [Hash] 小米返回数据结构
|
79
|
+
def post(url, params = nil)
|
80
|
+
r = @client.post(url, form: params)
|
81
|
+
data = JSON.parse(r)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def determine_platform!(env)
|
87
|
+
unless DEVICES.include?@device
|
88
|
+
raise RequestError, '必须使用 Xiaomi::Push::Android 或 Xiaomi::Push::IOS 实例化'
|
89
|
+
end
|
90
|
+
|
91
|
+
if env == :sandbox && @device == 'ANDROID'
|
92
|
+
raise RequestError, 'Android 环境不能支持 sandbox 测试环境'
|
93
|
+
end
|
40
94
|
end
|
41
95
|
end
|
42
96
|
end
|
data/lib/xiaomi/push/commands.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
command :feedback do |c|
|
2
2
|
c.syntax = 'xmp feedback [options]'
|
3
3
|
c.summary = '获取小米无效的设备列表'
|
4
|
-
c.description = ''
|
4
|
+
c.description = '获取小米无效的设备列表'
|
5
5
|
|
6
6
|
# normal params
|
7
|
-
c.option '--device DEVICE', %w(
|
7
|
+
c.option '--device DEVICE', %w(android, ios), '设备类型'
|
8
8
|
c.option '--secret SECRET', '应用密钥'
|
9
9
|
|
10
10
|
c.action do |args, options|
|
@@ -24,16 +24,15 @@ command :feedback do |c|
|
|
24
24
|
def feedback!
|
25
25
|
client = Xiaomi::Push.const_get(@device).new(@secret)
|
26
26
|
r = client.feedback.invalid
|
27
|
-
|
28
27
|
puts r
|
29
28
|
end
|
30
29
|
|
31
30
|
def determine_device!
|
32
31
|
devices = %w(Android iOS).freeze
|
33
|
-
@device = choose
|
32
|
+
@device = choose("选择推送设备:", *devices)
|
34
33
|
end
|
35
34
|
|
36
35
|
def determine_secret!
|
37
|
-
@secret ||= ask
|
36
|
+
@secret ||= ask('小米应用密钥:')
|
38
37
|
end
|
39
38
|
end
|
@@ -1,19 +1,23 @@
|
|
1
1
|
command :message do |c|
|
2
2
|
c.syntax = 'xmp message [options]'
|
3
3
|
c.summary = '发送小米推送消息'
|
4
|
-
c.description = '使用小米推送消息(目前仅支持 regid/alias/topic 推送方式)'
|
4
|
+
c.description = '使用小米推送消息(目前仅支持 regid/alias/user/topic 推送方式)'
|
5
5
|
|
6
|
-
#
|
7
|
-
c.option '--device DEVICE', %w(
|
6
|
+
# 必须参数
|
7
|
+
c.option '--device DEVICE', %w(android ios), '设备类型'
|
8
8
|
c.option '--secret SECRET', '应用密钥'
|
9
9
|
|
10
|
-
#
|
10
|
+
# 推送类型
|
11
11
|
c.option '--regid REGID', 'reg id'
|
12
12
|
c.option '--alias ALIAS', '别名'
|
13
|
-
c.option '--
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
c.option '--user USER', '用户'
|
14
|
+
c.option '--topic TOPIC', '标签/群组'
|
15
|
+
|
16
|
+
# 消息体
|
17
|
+
c.option '-i', '--title TITLE', '消息标题(适用于 Android 或 iOS 10 以上设备)'
|
18
|
+
c.option '-s', '--subtitle SUBTITLE', '消息副标题(仅适用于 iOS 10 以上设备)'
|
19
|
+
c.option '-m', '--image IMAGE', '消息图片地址(仅适用于 iOS 10 以上设备)'
|
20
|
+
# c.option '-c', '--image IMAGE', '消息图片地址(仅适用于 iOS 10 以上设备)'
|
17
21
|
c.option '-d', '--description DESCRIPTION', '消息主体描述'
|
18
22
|
c.option '-b', '--badge BADGE', Integer, '消息数字'
|
19
23
|
c.option '-e', '--extras KEY=VALUE', Array, '自定义数据(使用 KEY=VALUE 方式,多个以逗号不带空格分隔)'
|
@@ -62,7 +66,10 @@ command :message do |c|
|
|
62
66
|
|
63
67
|
def determine_ios_message!(options)
|
64
68
|
@message = Xiaomi::Push::Message::IOS.new(
|
69
|
+
title: @title,
|
70
|
+
subtitle: @subtitle,
|
65
71
|
description: @description,
|
72
|
+
image: @image,
|
66
73
|
badge: @badge,
|
67
74
|
extras: @extras
|
68
75
|
)
|
@@ -70,7 +77,9 @@ command :message do |c|
|
|
70
77
|
|
71
78
|
def determine_message!(options)
|
72
79
|
@title = options.title
|
80
|
+
@subtitle = options.subtitle
|
73
81
|
@description = options.description
|
82
|
+
@image = options.image
|
74
83
|
@badge = options.badge
|
75
84
|
|
76
85
|
@extras =
|
@@ -90,7 +99,7 @@ command :message do |c|
|
|
90
99
|
|
91
100
|
def determine_device!
|
92
101
|
devices = %w(Android iOS).freeze
|
93
|
-
@device = choose
|
102
|
+
@device = choose('选择推送设备:', *devices)
|
94
103
|
end
|
95
104
|
|
96
105
|
def determine_secret!
|
@@ -98,15 +107,15 @@ command :message do |c|
|
|
98
107
|
end
|
99
108
|
|
100
109
|
def determine_channel!(options)
|
101
|
-
channles = %w(regid alias topic).freeze
|
110
|
+
channles = %w(regid alias user topic).freeze
|
102
111
|
@channel = channles.select { |k| options.__hash__.key?k.to_sym }
|
103
112
|
|
104
113
|
if @channel.count > 0
|
105
114
|
@channel = @channel[0]
|
106
115
|
@channel_id = options.__hash__[@channel.to_sym]
|
107
116
|
else
|
108
|
-
@channel = choose
|
109
|
-
@channel_id = ask
|
117
|
+
@channel = choose('选择推送方式:', *channles)
|
118
|
+
@channel_id = ask("输入 #{@channel} 的值:")
|
110
119
|
end
|
111
120
|
end
|
112
121
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
command :user do |c|
|
2
|
+
c.syntax = 'xmp user [options]'
|
3
|
+
c.summary = '小米 aliases/topics 查询工具'
|
4
|
+
c.description = '通过 reg id 查找用户绑定的 alias 和 topic'
|
5
|
+
|
6
|
+
# normal params
|
7
|
+
c.option '--device DEVICE', %w(android, ios), '设备类型'
|
8
|
+
c.option '--secret SECRET', '应用密钥'
|
9
|
+
c.option '--reg-id REG_ID', 'Reg id'
|
10
|
+
c.option '--package-name PACKAGE_NAME', '包的唯一标识,android 是 package name, ios 是 bundle identiflier'
|
11
|
+
|
12
|
+
c.action do |args, options|
|
13
|
+
puts options if $verbose
|
14
|
+
|
15
|
+
@device = options.device.capitalize if options.device
|
16
|
+
@secret = options.secret
|
17
|
+
@reg_id = options.reg_id
|
18
|
+
@package_name = options.package_name
|
19
|
+
|
20
|
+
determine_device! unless @device
|
21
|
+
determine_secret! unless @secret
|
22
|
+
|
23
|
+
@client = Xiaomi::Push.const_get(@device).new(@secret)
|
24
|
+
|
25
|
+
find_aliases!
|
26
|
+
find_topics!
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def find_aliases!
|
32
|
+
r = @client.user.aliases(@reg_id, @package_name)
|
33
|
+
if r['result'] == 'ok'
|
34
|
+
print_data(r, :alias)
|
35
|
+
else
|
36
|
+
user_error(r, :alias)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_topics!
|
41
|
+
r = @client.user.topices(@reg_id, @package_name)
|
42
|
+
if r['result'] == 'ok'
|
43
|
+
print_data(r, :topic)
|
44
|
+
else
|
45
|
+
user_error(r, :topic)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def print_data(data, type)
|
50
|
+
say "#{type.to_s} count: #{data['data']['list'].count}"
|
51
|
+
data['data']['list'].each do |key|
|
52
|
+
say_ok " * #{key}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def user_error(data, type)
|
57
|
+
say_error "#{type.to_s} error."
|
58
|
+
say_error "[#{data['code']}] #{data['description']}: #{data['reason']}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def determine_device!
|
62
|
+
devices = %w(Android iOS).freeze
|
63
|
+
@device = choose("选择推送设备:", *devices)
|
64
|
+
end
|
65
|
+
|
66
|
+
def determine_secret!
|
67
|
+
@secret ||= ask('小米应用密钥:')
|
68
|
+
end
|
69
|
+
end
|
data/lib/xiaomi/push/const.rb
CHANGED
@@ -3,31 +3,49 @@ require 'uri'
|
|
3
3
|
module Xiaomi
|
4
4
|
module Push
|
5
5
|
module Const
|
6
|
+
# 支持设备
|
6
7
|
DEVICES = %w(ANDROID IOS).freeze
|
7
|
-
|
8
|
+
# 产品环境
|
8
9
|
PRODUCTION_URL = 'https://api.xmpush.xiaomi.com'.freeze
|
10
|
+
# 沙盒环境(仅支持 iOS)
|
9
11
|
SANDBOX_URL = 'https://sandbox.xmpush.xiaomi.com'.freeze
|
10
12
|
|
11
13
|
attr_reader :base_url
|
12
14
|
|
15
|
+
# 切换产品环境
|
13
16
|
def use_production!
|
14
17
|
production
|
15
18
|
end
|
16
19
|
|
20
|
+
# 切换沙盒环境
|
17
21
|
def use_sandbox!
|
18
22
|
sandbox
|
19
23
|
end
|
20
24
|
|
25
|
+
# :nodoc:
|
21
26
|
def production
|
22
27
|
@base_url ||= PRODUCTION_URL
|
23
28
|
end
|
24
29
|
|
30
|
+
# :nodoc:
|
25
31
|
def sandbox
|
26
32
|
@base_url ||= SANDBOX_URL
|
27
33
|
end
|
28
34
|
|
35
|
+
# :nodoc:
|
29
36
|
def build_uri(uri)
|
30
|
-
|
37
|
+
version =
|
38
|
+
if uri.start_with?('stats', 'trace', 'alias', 'reg_id') || uri == 'topic/all'
|
39
|
+
# 获取消息的统计数据/追踪消息状态/某个用户目前设置的所有 Alias 和订阅的所有 Topic
|
40
|
+
'v1'
|
41
|
+
elsif uri.start_with?('message')
|
42
|
+
# 发送消息支持多包使用 v3 版本
|
43
|
+
'v3'
|
44
|
+
else
|
45
|
+
'v2'
|
46
|
+
end
|
47
|
+
|
48
|
+
File.join(@base_url, version, uri)
|
31
49
|
end
|
32
50
|
end
|
33
51
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Xiaomi
|
2
|
+
module Push
|
3
|
+
# Android 推送
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# Xiaomi::Push::Message::Android.new(
|
7
|
+
# title:'标题要有吸引力',
|
8
|
+
# description:'描述可以在手机显示两行',
|
9
|
+
# notify_type:'DEFAULT_ALL',
|
10
|
+
# extras: {
|
11
|
+
# source: 'mpush'
|
12
|
+
# }
|
13
|
+
# )
|
14
|
+
class Android < Xiaomi::Push::Client
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Xiaomi
|
2
|
+
module Push
|
3
|
+
# iOS 推送
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# Xiaomi::Push::Message::iOS.new(
|
7
|
+
# description:'iOS 主要显示描述',
|
8
|
+
# badge:10,
|
9
|
+
# extras: {
|
10
|
+
# uri: 'app://bbs?id=8624',
|
11
|
+
# source: 'mpush'
|
12
|
+
# }
|
13
|
+
# )
|
14
|
+
class IOS < Xiaomi::Push::Client
|
15
|
+
end
|
16
|
+
|
17
|
+
# 用于 cli 的别名
|
18
|
+
Ios = IOS
|
19
|
+
end
|
20
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module Xiaomi
|
2
2
|
module Push
|
3
3
|
module Services
|
4
|
+
# 适用于 iOS 推送获取设备 Feedback API
|
5
|
+
#
|
6
|
+
# @attr [Client] context
|
4
7
|
class Feedback
|
5
8
|
attr_reader :context
|
6
9
|
|
@@ -8,10 +11,14 @@ module Xiaomi
|
|
8
11
|
@context = context
|
9
12
|
end
|
10
13
|
|
14
|
+
# 获取失效的 device token
|
15
|
+
#
|
16
|
+
# @see https://dev.mi.com/console/doc/detail?pId=1163#_4_1
|
17
|
+
#
|
18
|
+
# @return [Hash] 小米返回数据结构
|
11
19
|
def invalid
|
12
20
|
url = 'https://feedback.xmpush.xiaomi.com/v1/feedback/fetch_invalid_regids'
|
13
|
-
|
14
|
-
MultiJson.load r
|
21
|
+
@context.get(url)
|
15
22
|
end
|
16
23
|
end
|
17
24
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Xiaomi
|
2
|
+
module Push
|
3
|
+
module Services
|
4
|
+
# 定时任务类 API
|
5
|
+
#
|
6
|
+
# @see https://dev.mi.com/console/doc/detail?pId=1163#_7
|
7
|
+
#
|
8
|
+
# @attr [Client] context
|
9
|
+
class Job
|
10
|
+
attr_reader :context
|
11
|
+
|
12
|
+
def initialize(context)
|
13
|
+
@context = context
|
14
|
+
end
|
15
|
+
|
16
|
+
# 检测定时任务是否存在
|
17
|
+
#
|
18
|
+
# @param [String] job_id 任务 id
|
19
|
+
# @return [Hash] 小米返回数据结构
|
20
|
+
def exist?(job_id)
|
21
|
+
url = @context.build_uri('schedule_job/exist')
|
22
|
+
@context.post(url, { job_id: job_id })
|
23
|
+
end
|
24
|
+
|
25
|
+
# 删除定时任务
|
26
|
+
#
|
27
|
+
# @param [String] job_id 任务 id
|
28
|
+
# @return [Hash] 小米返回数据结构
|
29
|
+
def destory(job_id)
|
30
|
+
url = @context.build_uri('schedule_job/delete')
|
31
|
+
@context.post(url, { job_id: job_id })
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|