mochizuki 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +59 -0
- data/bin/mochizuki +7 -0
- data/lib/mochizuki.rb +12 -0
- data/lib/mochizuki/auto_query.rb +26 -0
- data/lib/mochizuki/configurable.rb +51 -0
- data/lib/mochizuki/error.rb +6 -0
- data/lib/mochizuki/fetcher.rb +81 -0
- data/lib/mochizuki/logging.rb +21 -0
- data/lib/mochizuki/status.rb +55 -0
- data/lib/mochizuki/telegram_bot.rb +45 -0
- data/lib/mochizuki/version.rb +5 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9267c9d3ffaa34e028901b0c0492651c7df87425dc1a23ce1223ae3c11a9c294
|
4
|
+
data.tar.gz: 2271c29c305f2de09d0ab6258028c694970d05111dad001fa91e3c9d5e71f45b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 476cdfc1d02ccef3802e88559747af2357d91c8aa81473a35c46b63d291554706f7a617c778f364c469afeecb994a87351ff5c21f403dcb8c94f41debe8f5804
|
7
|
+
data.tar.gz: b35815bcfab76a140026ac1ef026d101b51ca498c7b09c86e399058b354faf5aff0355b6bce24b3bb8060c04b844bafa60599d8cd8e6b9c16ff0b15a22649e9f
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2020 Kowalski Dark
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Mochizuki
|
2
|
+
|
3
|
+
同济大学能源管理中心电费自动查询 telegram bot
|
4
|
+
|
5
|
+
Manually/auto query and send alarms to channel when the power gets below your threshold.
|
6
|
+
|
7
|
+
![3](./screenshots/3.jpg)
|
8
|
+
![4](./screenshots/4.jpg)
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
### From RubyGems
|
13
|
+
|
14
|
+
```
|
15
|
+
gem install mochizuki
|
16
|
+
```
|
17
|
+
|
18
|
+
### Using docker
|
19
|
+
|
20
|
+
**Recommended**
|
21
|
+
|
22
|
+
See [mochizuki-docker](https://github.com/DarkKowalski/mochizuki-docker)
|
23
|
+
|
24
|
+
## Configuration
|
25
|
+
|
26
|
+
Place a `mochizuki.conf` in your directory and run `mochizuki`
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
Mochizuki.configure do |config|
|
30
|
+
config.bot_token = 'your_bot_token'
|
31
|
+
config.channel = '@your_channel'
|
32
|
+
config.query_interval = '300s'
|
33
|
+
config.alarm_threshold = '60' # kWh
|
34
|
+
|
35
|
+
config.campus = '1'
|
36
|
+
config.building = '1'
|
37
|
+
config.floor = '1'
|
38
|
+
config.dorm = '1'
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
**How to get the last four attributes**
|
43
|
+
|
44
|
+
Go to [同济大学能源管理中心](http://202.120.163.129:88) and fill the form
|
45
|
+
|
46
|
+
![1](./screenshots/1.png)
|
47
|
+
|
48
|
+
Open your dev tools and look at the bottom on the right
|
49
|
+
|
50
|
+
![2](./screenshots/2.png)
|
51
|
+
|
52
|
+
Those shitty words mean
|
53
|
+
|
54
|
+
```
|
55
|
+
drlouming -> campus
|
56
|
+
drceng -> building
|
57
|
+
dr_ceng -> floor
|
58
|
+
drfangjian -> dorm
|
59
|
+
```
|
data/bin/mochizuki
ADDED
data/lib/mochizuki.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'mochizuki/error'
|
4
|
+
|
5
|
+
require_relative 'mochizuki/version'
|
6
|
+
require_relative 'mochizuki/logging'
|
7
|
+
require_relative 'mochizuki/configurable'
|
8
|
+
|
9
|
+
require_relative 'mochizuki/auto_query'
|
10
|
+
require_relative 'mochizuki/fetcher'
|
11
|
+
require_relative 'mochizuki/status'
|
12
|
+
require_relative 'mochizuki/telegram_bot'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rufus-scheduler'
|
4
|
+
|
5
|
+
module Mochizuki
|
6
|
+
class AutoQuery
|
7
|
+
def initialize(logger = Mochizuki.logger, config = Mochizuki.config)
|
8
|
+
@logger = logger
|
9
|
+
@config = config
|
10
|
+
|
11
|
+
Mochizuki::Fetcher.new.fetch # update Mochizuki.status
|
12
|
+
end
|
13
|
+
|
14
|
+
def alarm
|
15
|
+
scheduler = Rufus::Scheduler.new
|
16
|
+
scheduler.every @config.query_interval.to_s do
|
17
|
+
power = Mochizuki::Fetcher.new.fetch
|
18
|
+
@logger.info "Auto query, #{power} kWh remaining"
|
19
|
+
if Mochizuki.status.auto_alarm_enabled?
|
20
|
+
yield(power)
|
21
|
+
Mochizuki.status.alarmed_before = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mochizuki
|
4
|
+
module Configurable
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def config
|
11
|
+
@config ||= Configuration.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset_config
|
15
|
+
@config = Configuration.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def configure
|
19
|
+
yield(config)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Configuration
|
24
|
+
attr_accessor :bot_token, :channel,
|
25
|
+
:api_host, :api_port,
|
26
|
+
:query_interval, :alarm_threshold,
|
27
|
+
:campus, :building, :floor, :dorm
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@bot_token = nil
|
31
|
+
@channel = nil
|
32
|
+
|
33
|
+
@api_host = '202.120.163.129' # https://nyglzx.tongji.edu.cn
|
34
|
+
@api_port = '88'
|
35
|
+
|
36
|
+
@query_interval = '300s'
|
37
|
+
@alarm_threshold = '60' # kWh
|
38
|
+
|
39
|
+
# shitty names from legacy code
|
40
|
+
@campus = nil # -> drlouming
|
41
|
+
@building = nil # -> drceng
|
42
|
+
@floor = nil # -> dr_ceng
|
43
|
+
@dorm = nil # -> drfangjian
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module Mochizuki
|
50
|
+
include Mochizuki::Configurable
|
51
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'uri'
|
5
|
+
require 'nokogiri'
|
6
|
+
|
7
|
+
module Mochizuki
|
8
|
+
class Fetcher
|
9
|
+
def initialize(logger = Mochizuki.logger, config = Mochizuki.config)
|
10
|
+
@logger = logger
|
11
|
+
@config = config
|
12
|
+
|
13
|
+
@uri = URI("http://#{@config.api_host}:#{config.api_port}")
|
14
|
+
|
15
|
+
@request_body = {
|
16
|
+
'__EVENTTARGET' => '',
|
17
|
+
'__EVENTARGUMENT' => '',
|
18
|
+
'__LASTFOCUS' => '',
|
19
|
+
'__VIEWSTATE' => '',
|
20
|
+
'__VIEWSTATEGENERATOR' => 'CA0B0334',
|
21
|
+
'drlouming' => '',
|
22
|
+
'drceng' => '',
|
23
|
+
'dr_ceng' => '',
|
24
|
+
'drfangjian' => '',
|
25
|
+
'radio' => 'usedR',
|
26
|
+
'ImageButton1.x' => '0',
|
27
|
+
'ImageButton1.y' => '0'
|
28
|
+
}
|
29
|
+
|
30
|
+
@cookie = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch
|
34
|
+
fetch_viewstate
|
35
|
+
fetch_cookie
|
36
|
+
power = fetch_power
|
37
|
+
|
38
|
+
if power.nil?
|
39
|
+
@logger.warn 'Failed to query.'
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
Mochizuki.update_status(power)
|
44
|
+
power
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def fetch_viewstate
|
50
|
+
@request_body['drlouming'] = @config.campus
|
51
|
+
@request_body['drceng'] = @config.building
|
52
|
+
@request_body['dr_ceng'] = @config.floor
|
53
|
+
@request_body['drfangjian'] = @config.dorm
|
54
|
+
|
55
|
+
3.times do
|
56
|
+
resp = Faraday.post(@uri) do |req|
|
57
|
+
req.headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
58
|
+
req.body = URI.encode_www_form(@request_body)
|
59
|
+
end
|
60
|
+
html = Nokogiri::HTML(resp.body)
|
61
|
+
@request_body['__VIEWSTATE'] = html.at_css('input#__VIEWSTATE')['value']
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def fetch_cookie
|
66
|
+
resp = Faraday.post(@uri) do |req|
|
67
|
+
req.headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
68
|
+
req.body = URI.encode_www_form(@request_body)
|
69
|
+
end
|
70
|
+
@cookie = resp.headers['set-cookie'].split('; ')[0]
|
71
|
+
end
|
72
|
+
|
73
|
+
def fetch_power
|
74
|
+
resp = Faraday.get("#{@uri}/usedRecord1.aspx") do |req|
|
75
|
+
req.headers = { 'Cookie' => @cookie, 'Content-Type' => 'application/x-www-form-urlencoded' }
|
76
|
+
end
|
77
|
+
html = Nokogiri::HTML(resp.body)
|
78
|
+
html.xpath('//h6').text.scan(/(\d+[,.]\d+)/)[0][0]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Mochizuki
|
6
|
+
module Logging
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def logger
|
13
|
+
@logger ||= Logger.new($stdout)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Mochizuki
|
20
|
+
include Logging
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mochizuki
|
4
|
+
module Status
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def status
|
11
|
+
@status ||= Status.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset_status
|
15
|
+
@status = Status.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def update_status(power)
|
19
|
+
last = Mochizuki.status.below_threshold
|
20
|
+
current = power.to_f < @config.alarm_threshold.to_f
|
21
|
+
@logger.info "Status, @below_threshold, last: #{last}, current: #{current}"
|
22
|
+
|
23
|
+
Mochizuki.status.below_threshold = current
|
24
|
+
|
25
|
+
return unless !current && last # re-enabled auto alarm
|
26
|
+
|
27
|
+
Mochizuki.status.alarmed_before = false
|
28
|
+
@logger.info "Status, @alarmed_before, #{@alarmed_before}, auto alarm enabled"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Status
|
33
|
+
attr_accessor :below_threshold, :alarmed_before
|
34
|
+
|
35
|
+
def initialize(logger = Mochizuki.logger)
|
36
|
+
@logger = logger
|
37
|
+
@below_threshold = nil
|
38
|
+
@alarmed_before = false
|
39
|
+
end
|
40
|
+
|
41
|
+
def auto_alarm_enabled?
|
42
|
+
if @below_threshold.nil?
|
43
|
+
@logger.error "Unable to check status, @below_threshold can't be nil"
|
44
|
+
raise Mochizuki::Error, 'Invalid @below_threshold'
|
45
|
+
end
|
46
|
+
|
47
|
+
@below_threshold && !@alarmed_before
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module Mochizuki
|
54
|
+
include Status
|
55
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'telegram/bot'
|
4
|
+
|
5
|
+
module Mochizuki
|
6
|
+
class Bot
|
7
|
+
def initialize(logger = Mochizuki.logger, config = Mochizuki.config)
|
8
|
+
@config = config
|
9
|
+
@logger = logger
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
Telegram::Bot::Client.run(@config.bot_token) do |bot|
|
14
|
+
Mochizuki::AutoQuery.new.alarm do |power|
|
15
|
+
bot.api.send_message(chat_id: @config.channel,
|
16
|
+
text: "Dorm: #{@config.dorm}: #{power} kWh, " \
|
17
|
+
"lower than threshold #{@config.alarm_threshold}")
|
18
|
+
end
|
19
|
+
@logger.info 'Auto query enabled'
|
20
|
+
|
21
|
+
bot.listen do |msg|
|
22
|
+
case msg.text
|
23
|
+
when '/start'
|
24
|
+
bot.api.send_message(chat_id: msg.chat.id,
|
25
|
+
text: 'I am a telegram bot written in ruby, '\
|
26
|
+
'details at https://github.com/DarkKowalski/mochizuki-bot')
|
27
|
+
@logger.info "ID: #{msg.chat.id} uses /start"
|
28
|
+
when '/query'
|
29
|
+
dorm = @config.dorm
|
30
|
+
begin
|
31
|
+
power = Mochizuki::Fetcher.new.fetch
|
32
|
+
send_msg = "Dorm #{dorm}: #{power} kWh"
|
33
|
+
rescue StandardError
|
34
|
+
send_msg = 'Failed to query.'
|
35
|
+
end
|
36
|
+
bot.api.send_message(chat_id: msg.chat.id, text: send_msg)
|
37
|
+
else
|
38
|
+
bot.api.send_message(chat_id: msg.chat.id, text: "Use '/query' to get remaining power")
|
39
|
+
@logger.warn "ID: #{msg.chat.id} uses #{msg.text}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mochizuki
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kowalski Dark
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-09-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.10.10
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.10.10
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rufus-scheduler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.6.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.6.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: telegram-bot-ruby
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.10.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.10.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ci_reporter_minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.0.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.0.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 5.14.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 5.14.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 13.0.1
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 13.0.1
|
111
|
+
description: Get the amount of electricity remaining for dorms from Tongxinxun
|
112
|
+
email:
|
113
|
+
- DarkKowalski2012@gmail.com
|
114
|
+
executables:
|
115
|
+
- mochizuki
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- LICENSE
|
120
|
+
- README.md
|
121
|
+
- bin/mochizuki
|
122
|
+
- lib/mochizuki.rb
|
123
|
+
- lib/mochizuki/auto_query.rb
|
124
|
+
- lib/mochizuki/configurable.rb
|
125
|
+
- lib/mochizuki/error.rb
|
126
|
+
- lib/mochizuki/fetcher.rb
|
127
|
+
- lib/mochizuki/logging.rb
|
128
|
+
- lib/mochizuki/status.rb
|
129
|
+
- lib/mochizuki/telegram_bot.rb
|
130
|
+
- lib/mochizuki/version.rb
|
131
|
+
homepage: https://github.com/darkkowalski/mochizuki-bot
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata:
|
135
|
+
bug_tracker_uri: https://github.com/darkkowalski/mochizuki-bot/issues
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 2.7.1
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubygems_version: 3.1.2
|
152
|
+
signing_key:
|
153
|
+
specification_version: 4
|
154
|
+
summary: Tongxinyun Electircity Remaining
|
155
|
+
test_files: []
|