waylon-core 0.1.1 → 0.1.2
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/.rubocop.yml +8 -1
- data/.ruby-version +1 -1
- data/Gemfile.lock +9 -9
- data/lib/waylon/condition.rb +26 -1
- data/lib/waylon/conditions/black_hole.rb +34 -0
- data/lib/waylon/conditions/regex.rb +5 -0
- data/lib/waylon/config.rb +3 -2
- data/lib/waylon/core.rb +5 -0
- data/lib/waylon/group.rb +1 -1
- data/lib/waylon/logger.rb +1 -1
- data/lib/waylon/message.rb +19 -0
- data/lib/waylon/route.rb +1 -1
- data/lib/waylon/routes/black_hole.rb +17 -0
- data/lib/waylon/rspec/matchers/route_matcher.rb +7 -0
- data/lib/waylon/rspec/skill.rb +1 -1
- data/lib/waylon/rspec/test_message.rb +2 -0
- data/lib/waylon/rspec/test_sense.rb +21 -15
- data/lib/waylon/rspec/test_worker.rb +2 -2
- data/lib/waylon/sense.rb +5 -6
- data/lib/waylon/sense_registry.rb +2 -0
- data/lib/waylon/skill.rb +54 -21
- data/lib/waylon/skill_registry.rb +47 -10
- data/lib/waylon/skills/default.rb +5 -0
- data/lib/waylon/skills/diagnostics.rb +58 -0
- data/lib/waylon/skills/fun.rb +5 -2
- data/lib/waylon/skills/groups.rb +167 -0
- data/lib/waylon/skills/help.rb +174 -0
- data/lib/waylon/user.rb +5 -0
- data/lib/waylon/version.rb +1 -1
- data/lib/waylon/webhook.rb +24 -25
- data/lib/waylon.rb +3 -0
- data/waylon-core.gemspec +1 -1
- metadata +10 -5
@@ -5,6 +5,8 @@ module Waylon
|
|
5
5
|
class SkillRegistry
|
6
6
|
include Singleton
|
7
7
|
|
8
|
+
attr_reader :routes
|
9
|
+
|
8
10
|
# A wrapper around the singleton #register method
|
9
11
|
# @param name [String] The name of the skill in the registry
|
10
12
|
# @param class_name [Class] The class to associate with the name
|
@@ -14,8 +16,24 @@ module Waylon
|
|
14
16
|
instance.register(name, class_name, condition)
|
15
17
|
end
|
16
18
|
|
17
|
-
def
|
18
|
-
|
19
|
+
def self.find_by_name(name)
|
20
|
+
[
|
21
|
+
*instance.routes,
|
22
|
+
Routes::PermissionDenied.new,
|
23
|
+
Routes::BlackHole.new,
|
24
|
+
Routes::Default.new
|
25
|
+
].find { |r| r.name == name.to_s }
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.route(message)
|
29
|
+
instance.route(message)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Provides the default route based on the received message.
|
33
|
+
# @param message [Waylon::Message] The received message
|
34
|
+
# @return [Waylon::Route]
|
35
|
+
def default_route(message)
|
36
|
+
message.to_bot? ? Routes::Default.new : Routes::BlackHole.new
|
19
37
|
end
|
20
38
|
|
21
39
|
# Gathers a Hash of help data for all routes a user is permitted to access
|
@@ -23,9 +41,13 @@ module Waylon
|
|
23
41
|
# @return [Hash]
|
24
42
|
def help(user)
|
25
43
|
data = {}
|
26
|
-
@routes.select { |r| r.permits?(user) }.each do |permitted|
|
27
|
-
data[permitted.destination.
|
28
|
-
data[permitted.destination.
|
44
|
+
@routes.select { |r| r.permits?(user) && r.mention_only? }.each do |permitted|
|
45
|
+
data[permitted.destination.component_namespace] ||= []
|
46
|
+
data[permitted.destination.component_namespace] << if permitted.help
|
47
|
+
{ name: permitted.name, help: permitted.help }
|
48
|
+
else
|
49
|
+
{ name: permitted.name }
|
50
|
+
end
|
29
51
|
end
|
30
52
|
|
31
53
|
data.reject { |_k, v| v.empty? }
|
@@ -53,14 +75,25 @@ module Waylon
|
|
53
75
|
# Given a message, find a suitable skill Route for it (sorted by priority, highest first)
|
54
76
|
# @param message [Waylon::Message] A Message instance
|
55
77
|
# @return [Hash]
|
78
|
+
# rubocop:disable Metrics/AbcSize
|
79
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
80
|
+
# rubocop:disable Metrics/MethodLength
|
81
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
56
82
|
def route(message)
|
57
83
|
route = nil
|
58
|
-
message_text = message.
|
84
|
+
message_text = message.body.strip
|
59
85
|
@routes ||= []
|
60
|
-
@routes.sort_by(&:priority).reverse.each do |
|
61
|
-
if
|
62
|
-
|
63
|
-
|
86
|
+
@routes.sort_by(&:priority).reverse.each do |this_route|
|
87
|
+
if this_route.permits?(message.author) &&
|
88
|
+
this_route.matches?(message_text) &&
|
89
|
+
(this_route.properly_mentions?(message) || message.private?)
|
90
|
+
route = this_route
|
91
|
+
elsif this_route.permits?(message.author) &&
|
92
|
+
this_route.matches?(message_text) &&
|
93
|
+
!this_route.properly_mentions?(message)
|
94
|
+
# Black hole these because they're not direct mentions
|
95
|
+
route = Routes::BlackHole.new
|
96
|
+
elsif this_route.matches?(message_text)
|
64
97
|
route = Routes::PermissionDenied.new
|
65
98
|
end
|
66
99
|
if route
|
@@ -70,5 +103,9 @@ module Waylon
|
|
70
103
|
end
|
71
104
|
route
|
72
105
|
end
|
106
|
+
# rubocop:enable Metrics/AbcSize
|
107
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
108
|
+
# rubocop:enable Metrics/MethodLength
|
109
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
73
110
|
end
|
74
111
|
end
|
@@ -20,6 +20,11 @@ module Waylon
|
|
20
20
|
reply("#{prefix} #{responses.sample} #{help_postfix}")
|
21
21
|
end
|
22
22
|
|
23
|
+
# This action ignores messages
|
24
|
+
def ignore
|
25
|
+
log("Ignoring black-holed message from #{message.author.email}")
|
26
|
+
end
|
27
|
+
|
23
28
|
# A useful addition to message to tell the User how to get help
|
24
29
|
# @return [String]
|
25
30
|
def help_postfix
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Waylon
|
4
|
+
module Skills
|
5
|
+
# Built-in info routes
|
6
|
+
class Diagnostics < Skill
|
7
|
+
# Say hello to Waylon
|
8
|
+
route(
|
9
|
+
/^diagnostics|status$/i,
|
10
|
+
:status,
|
11
|
+
help: {
|
12
|
+
usage: "diagnostics|status",
|
13
|
+
description: "Retrieve this bot's current status"
|
14
|
+
}
|
15
|
+
)
|
16
|
+
|
17
|
+
# Provides info about Waylon's status
|
18
|
+
def status # rubocop:disable Metrics/AbcSize
|
19
|
+
response = []
|
20
|
+
response << "*Framework Version:* Waylon v#{Waylon::Core::VERSION}"
|
21
|
+
response << "*Sense plugins:*"
|
22
|
+
loaded_senses.each { |c| response << " - #{c}" }
|
23
|
+
response << "*Skill plugins:*"
|
24
|
+
loaded_routes.each { |d| response << " - #{d}" }
|
25
|
+
response << "*Redis:*"
|
26
|
+
state, read_time, write_time = test_redis
|
27
|
+
response << " - *Test Result:* #{state ? "Success" : "Error"}"
|
28
|
+
response << " - *Read time:* #{read_time}s"
|
29
|
+
response << " - *Write time:* #{write_time}s"
|
30
|
+
response << "*Queue Monitoring:*"
|
31
|
+
response << " - Failed jobs: #{Resque::Failure.count}"
|
32
|
+
|
33
|
+
reply response.join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
def loaded_routes
|
37
|
+
SkillRegistry.instance.routes.map { |r| r.destination.name }.sort.uniq
|
38
|
+
end
|
39
|
+
|
40
|
+
def loaded_senses
|
41
|
+
SenseRegistry.instance.senses.map { |_s, c| c.name }.sort.uniq
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_redis
|
45
|
+
test_key = ("a".."z").to_a.sample(10).join
|
46
|
+
test_value = (0..1000).to_a.sample(20).map(&:to_s).join
|
47
|
+
test_result = nil
|
48
|
+
|
49
|
+
write_time = Benchmark.realtime { db.store(test_key, test_value) }
|
50
|
+
read_time = Benchmark.realtime { test_result = db.load(test_key) }
|
51
|
+
|
52
|
+
db.delete(test_key)
|
53
|
+
|
54
|
+
[(test_value == test_result), read_time.round(6), write_time.round(6)]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/waylon/skills/fun.rb
CHANGED
@@ -6,8 +6,9 @@ module Waylon
|
|
6
6
|
class Fun < Skill
|
7
7
|
# Say hello to Waylon
|
8
8
|
route(
|
9
|
-
/^(hello|hi)
|
10
|
-
:hello
|
9
|
+
/^(hello|hi)([.!]+)?$/i,
|
10
|
+
:hello,
|
11
|
+
help: "hi|hello"
|
11
12
|
)
|
12
13
|
|
13
14
|
# Responds to "hello" in less boring ways
|
@@ -19,6 +20,8 @@ module Waylon
|
|
19
20
|
"How can I be of service?"
|
20
21
|
]
|
21
22
|
|
23
|
+
react :wave
|
24
|
+
|
22
25
|
reply responses.sample
|
23
26
|
end
|
24
27
|
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Waylon
|
4
|
+
module Skills
|
5
|
+
# Built-in skills for managing groups
|
6
|
+
class Groups < Skill
|
7
|
+
route(
|
8
|
+
/^add (.+) to (.+)$/,
|
9
|
+
:add_to_group,
|
10
|
+
help: {
|
11
|
+
usage: "add USER[,USER] to GROUP",
|
12
|
+
description: "Add USER(s) to a GROUP"
|
13
|
+
},
|
14
|
+
allowed_groups: %i[admins group_admins]
|
15
|
+
)
|
16
|
+
|
17
|
+
route(
|
18
|
+
/^remove (.+) from (.+)$/,
|
19
|
+
:remove_from_group,
|
20
|
+
help: {
|
21
|
+
usage: "remove USER[,USER] from GROUP",
|
22
|
+
description: "Remove USER(s) from a GROUP"
|
23
|
+
},
|
24
|
+
allowed_groups: %i[admins group_admins]
|
25
|
+
)
|
26
|
+
|
27
|
+
route(
|
28
|
+
/^(describe|list|print|show) (all )?(groups|group memberships)$/,
|
29
|
+
:list_all_groups,
|
30
|
+
help: {
|
31
|
+
usage: "list all groups",
|
32
|
+
description: "List all groups and their members"
|
33
|
+
},
|
34
|
+
allowed_groups: %i[admins group_admins]
|
35
|
+
)
|
36
|
+
|
37
|
+
route(
|
38
|
+
/^cleanup groups$/,
|
39
|
+
:cleanup_groups,
|
40
|
+
help: {
|
41
|
+
usage: "cleanup groups",
|
42
|
+
description: "Remove empty groups"
|
43
|
+
},
|
44
|
+
allowed_groups: %i[admins group_admins]
|
45
|
+
)
|
46
|
+
|
47
|
+
route(
|
48
|
+
/^((list )?my )?groups$/,
|
49
|
+
:list_my_groups,
|
50
|
+
help: {
|
51
|
+
usage: "list my groups",
|
52
|
+
description: "List my group memberships"
|
53
|
+
}
|
54
|
+
)
|
55
|
+
|
56
|
+
def add_to_group # rubocop:disable Metrics/AbcSize
|
57
|
+
user_list = tokens.first
|
58
|
+
group_name = tokens.last
|
59
|
+
|
60
|
+
if group_name == "global admins"
|
61
|
+
reply "Sorry, I can't manipulate global admins this way..."
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
group = Group.new(group_name)
|
66
|
+
|
67
|
+
log "Adding #{user_list} to group #{group}"
|
68
|
+
|
69
|
+
failures = []
|
70
|
+
user_list.split(",").each do |this_user|
|
71
|
+
found = found_user(this_user)
|
72
|
+
failures << found unless group.add(found)
|
73
|
+
end
|
74
|
+
|
75
|
+
unless failures.empty?
|
76
|
+
text = failures.size > 1 ? "were already members" : "was already a member"
|
77
|
+
reply "Looks like [#{failures.map { |u| mention(u) }.join(", ")}] #{text} of #{group_name}"
|
78
|
+
end
|
79
|
+
|
80
|
+
reply("Done adding users to #{group_name}!")
|
81
|
+
end
|
82
|
+
|
83
|
+
def cleanup_groups
|
84
|
+
# perform a key scan in Redis for all group keys and find empty groups
|
85
|
+
group_keys = all_group_keys.select do |group|
|
86
|
+
name = group.split(".").last
|
87
|
+
Group.new(name).to_a.empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
# delete the empty group keys
|
91
|
+
group_keys.each { |g| db.delete(g) }
|
92
|
+
|
93
|
+
group_names = group_keys.map { |g| g.split(".").last }
|
94
|
+
|
95
|
+
reply "I removed these empty groups: #{group_names.join(", ")}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def remove_from_group # rubocop:disable Metrics/AbcSize
|
99
|
+
user_list = tokens.first
|
100
|
+
group_name = tokens.last
|
101
|
+
|
102
|
+
if group_name == "global admins"
|
103
|
+
reply "Sorry, I can't manipulate global admins this way..."
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
group = Group.new(group_name)
|
108
|
+
|
109
|
+
log "Removing #{user_list} from group '#{group_name}'", :debug
|
110
|
+
|
111
|
+
failures = []
|
112
|
+
user_list.split(",").each do |this_user|
|
113
|
+
found = found_user(this_user)
|
114
|
+
failures << found unless group.remove(found)
|
115
|
+
end
|
116
|
+
|
117
|
+
unless failures.empty?
|
118
|
+
text = failures.size > 1 ? "were members" : "was a member"
|
119
|
+
reply("I don't think [#{failures.map { |u| mention(u) }.join(", ")}] #{text} of #{group_name}")
|
120
|
+
end
|
121
|
+
reply("Done removing users from groups!")
|
122
|
+
end
|
123
|
+
|
124
|
+
def list_all_groups
|
125
|
+
groups = {}
|
126
|
+
groups["global admins"] = global_admins unless global_admins.empty?
|
127
|
+
all_group_keys.each do |group|
|
128
|
+
name = group.split(".").last
|
129
|
+
groups[name] = Group.new(name).members
|
130
|
+
end
|
131
|
+
|
132
|
+
reply(codify(groups.to_yaml))
|
133
|
+
end
|
134
|
+
|
135
|
+
def list_my_groups
|
136
|
+
groups = []
|
137
|
+
groups << "global admins" if global_admins.include?(message.author.email)
|
138
|
+
all_group_keys.each do |group|
|
139
|
+
name = group.split(".").last
|
140
|
+
groups << name if Group.new(name).include?(message.author)
|
141
|
+
end
|
142
|
+
|
143
|
+
reply(codify(groups.to_yaml))
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def all_group_keys
|
149
|
+
db.adapter.backend.keys("groups.*")
|
150
|
+
end
|
151
|
+
|
152
|
+
def found_user(user_string)
|
153
|
+
if user_string =~ /^.+@.+/
|
154
|
+
# If provided an email
|
155
|
+
sense.user_class.find_by_email(user_string)
|
156
|
+
else
|
157
|
+
# Otherwise assume we're provided their user ID
|
158
|
+
sense.user_class.from_mention(user_string)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def global_admins
|
163
|
+
Config.instance.admins
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Waylon
|
4
|
+
module Skills
|
5
|
+
# A Skill for providing help
|
6
|
+
class Help < Skill
|
7
|
+
# Ask for some help.
|
8
|
+
# Defaults to all things a user has access to, but allows specifying either a Skill name or a Skill and Route
|
9
|
+
route(
|
10
|
+
/^help(?<skill_clause>\s+(?<skill>\w+)(?<action_clause>#(?<action>\w+)|\s+(?<action>\w+))?)?$/i,
|
11
|
+
:help,
|
12
|
+
help: {
|
13
|
+
usage: "help [skill [action]]",
|
14
|
+
description: "Allows asking for help, either for all skills or for a particular skill or action"
|
15
|
+
}
|
16
|
+
)
|
17
|
+
|
18
|
+
# Responds to "help" requests
|
19
|
+
def help
|
20
|
+
skill = named_tokens[:skill]
|
21
|
+
action = named_tokens[:action]
|
22
|
+
|
23
|
+
react :book
|
24
|
+
|
25
|
+
immediate_responses = [
|
26
|
+
"I'll send you a DM to go over that with you.",
|
27
|
+
"I'll DM you the details.",
|
28
|
+
"Look for a private message with those details.",
|
29
|
+
"You should have a private message with that information shortly."
|
30
|
+
]
|
31
|
+
|
32
|
+
# Only send this if you aren't already in a DM
|
33
|
+
threaded_reply "#{acknowledgement} #{immediate_responses.sample}" unless message.private?
|
34
|
+
|
35
|
+
if sense.supports?(:blocks)
|
36
|
+
reply_with_blocks(help_blocks(skill, action), private: true)
|
37
|
+
else
|
38
|
+
reply(help_text(skill, action), private: true)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def help_text(skill = nil, action = nil) # rubocop:disable Metrics/AbcSize
|
43
|
+
allowed_routes = SkillRegistry.instance.help(message.author)
|
44
|
+
resp = []
|
45
|
+
if skill
|
46
|
+
if action
|
47
|
+
resp << "## Help for #{skill}##{action}:"
|
48
|
+
this_route = allowed_routes[skill].find do |r|
|
49
|
+
r[:name].to_s == "#{skill}##{action}"
|
50
|
+
end
|
51
|
+
return "I couldn't find #{action} on #{skill}..." unless this_route
|
52
|
+
|
53
|
+
resp << build_help_text(this_route)
|
54
|
+
else
|
55
|
+
resp << "## Help for #{skill}:\n"
|
56
|
+
routes = allowed_routes[skill]
|
57
|
+
(routes || []).each { |r| resp << build_help_text(r) }
|
58
|
+
end
|
59
|
+
else
|
60
|
+
help_text_for_all(allowed_routes)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def help_blocks(skill = nil, action = nil) # rubocop:disable Metrics/AbcSize
|
65
|
+
allowed_routes = SkillRegistry.instance.help(message.author)
|
66
|
+
if skill
|
67
|
+
if action
|
68
|
+
this_route = allowed_routes[skill].find do |r|
|
69
|
+
r[:name].to_s == "#{skill}##{action}"
|
70
|
+
end
|
71
|
+
|
72
|
+
return not_found_block(skill, action) unless this_route
|
73
|
+
|
74
|
+
[build_header_block(skill, action), build_help_block(this_route)]
|
75
|
+
else
|
76
|
+
routes = allowed_routes[skill]
|
77
|
+
return not_found_block(skill, nil) unless routes
|
78
|
+
|
79
|
+
resp = [build_header_block(skill, nil)]
|
80
|
+
routes.each { |r| resp << build_help_block(r) }
|
81
|
+
resp
|
82
|
+
end
|
83
|
+
else
|
84
|
+
allowed_routes = SkillRegistry.instance.help(message.author)
|
85
|
+
help_blocks_for_all(allowed_routes)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def help_blocks_for_all(routes)
|
90
|
+
resp = [
|
91
|
+
{ type: "header", text: { type: "plain_text", text: "All known actions:", emoji: true } }
|
92
|
+
]
|
93
|
+
|
94
|
+
routes.each do |k, v|
|
95
|
+
resp += [
|
96
|
+
{ type: "divider" },
|
97
|
+
{
|
98
|
+
type: "section",
|
99
|
+
text: {
|
100
|
+
type: "mrkdwn",
|
101
|
+
text: "*Actions for '#{k}':*"
|
102
|
+
}
|
103
|
+
}
|
104
|
+
]
|
105
|
+
v.each { |r| resp << build_help_block(r) }
|
106
|
+
end
|
107
|
+
resp
|
108
|
+
end
|
109
|
+
|
110
|
+
def help_text_for_all(routes)
|
111
|
+
resp = []
|
112
|
+
resp << "*All known actions:*\n"
|
113
|
+
routes.each do |k, v|
|
114
|
+
resp << "* *#{k}*:"
|
115
|
+
v.each do |r|
|
116
|
+
resp << build_help_text(r)
|
117
|
+
end
|
118
|
+
resp << " --- "
|
119
|
+
end
|
120
|
+
resp.join("\n")
|
121
|
+
end
|
122
|
+
|
123
|
+
def build_header_block(skill, action)
|
124
|
+
text = if action
|
125
|
+
"Help for #{skill}##{action}:"
|
126
|
+
else
|
127
|
+
"Help for #{skill}:"
|
128
|
+
end
|
129
|
+
{ type: "header", text: { type: "plain_text", text: text, emoji: true } }
|
130
|
+
end
|
131
|
+
|
132
|
+
def build_help_block(this_route)
|
133
|
+
{
|
134
|
+
type: "section",
|
135
|
+
text: {
|
136
|
+
type: "mrkdwn",
|
137
|
+
text: " *#{this_route[:name]}*\n#{build_help_text(this_route)}"
|
138
|
+
}
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
def build_help_text(this_route)
|
143
|
+
route_help_text = []
|
144
|
+
case this_route[:help]
|
145
|
+
when String
|
146
|
+
route_help_text << " *Usage:* #{this_route[:help]}"
|
147
|
+
when Hash
|
148
|
+
route_help_text << " *Usage:* #{this_route.dig(:help, :usage)}"
|
149
|
+
if this_route.dig(:help, :description)
|
150
|
+
route_help_text << "\n *Description:* #{this_route.dig(:help, :description)}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
route_help_text.join
|
154
|
+
end
|
155
|
+
|
156
|
+
def not_found_block(skill, action)
|
157
|
+
sentence = if action
|
158
|
+
"I couldn't find any '#{action}' action on the '#{skill}' skill..."
|
159
|
+
else
|
160
|
+
"I couldn't find any routes related to a '#{skill}' skill..."
|
161
|
+
end
|
162
|
+
[
|
163
|
+
{
|
164
|
+
type: "section",
|
165
|
+
text: {
|
166
|
+
type: "mrkdwn",
|
167
|
+
text: sentence
|
168
|
+
}
|
169
|
+
}
|
170
|
+
]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/lib/waylon/user.rb
CHANGED
@@ -18,6 +18,11 @@ module Waylon
|
|
18
18
|
raise NotImplementedError, "find_by_handle isn't implemented"
|
19
19
|
end
|
20
20
|
|
21
|
+
# This should be overridden by subclasses to provide a mechanism for looking up Users based on mention strings
|
22
|
+
def from_mention(_mention_string)
|
23
|
+
raise NotImplementedError, "from_mention isn't implemented"
|
24
|
+
end
|
25
|
+
|
21
26
|
# Provides a simple mechanism for referencing User subclass's Sense
|
22
27
|
# @return [Class] A Sense subclass
|
23
28
|
def sense
|
data/lib/waylon/version.rb
CHANGED
data/lib/waylon/webhook.rb
CHANGED
@@ -21,7 +21,7 @@ module Waylon
|
|
21
21
|
# @return [Class] The name of the corresponding Sense class
|
22
22
|
def sense_class
|
23
23
|
last = self.class.name.split("::").last
|
24
|
-
Module.const_get("Senses::#{last}")
|
24
|
+
Module.const_get("Waylon::Senses::#{last}")
|
25
25
|
end
|
26
26
|
|
27
27
|
# This must be implemented on every Webhook to provide a mechanism to ensure received payloads are legit
|
@@ -38,36 +38,35 @@ module Waylon
|
|
38
38
|
end
|
39
39
|
|
40
40
|
before do
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
unless request.get? || request.options?
|
45
|
-
request.body.rewind
|
46
|
-
@parsed_body = JSON.parse(request.body.read, symbolize_names: true)
|
47
|
-
end
|
48
|
-
rescue StandardError => e
|
49
|
-
halt(400, { error: "Request must be JSON: #{e.message}" }.to_json)
|
41
|
+
unless request.get? || request.options?
|
42
|
+
request.body.rewind
|
43
|
+
@parsed_body = JSON.parse(request.body.read, symbolize_names: true)
|
50
44
|
end
|
45
|
+
rescue StandardError => e
|
46
|
+
content_type "application/json"
|
47
|
+
halt(400, { error: "Request must be JSON: #{e.message}" }.to_json)
|
51
48
|
end
|
52
49
|
|
53
50
|
after do
|
54
51
|
request.options? && headers("Access-Control-Allow-Methods" => @allowed_types || %w[OPTIONS POST])
|
55
52
|
end
|
56
53
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
54
|
+
## Example incoming webhook
|
55
|
+
#
|
56
|
+
# post "/" do
|
57
|
+
# begin
|
58
|
+
# request.body.rewind
|
59
|
+
# verify request.body.read, request.env
|
60
|
+
# enqueue @parsed_body
|
61
|
+
# rescue StandardError => e
|
62
|
+
# halt(422, { error: "Unprocessable entity: #{e.message}" }.to_json)
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# { status: :ok }.to_json
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# options "/" do
|
69
|
+
# halt 200
|
70
|
+
# end
|
72
71
|
end
|
73
72
|
end
|
data/lib/waylon.rb
CHANGED
data/waylon-core.gemspec
CHANGED
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.add_dependency "puma", "~> 5.5"
|
36
36
|
spec.add_dependency "resque", "~> 2.2"
|
37
37
|
|
38
|
-
spec.add_development_dependency "bundler", "~> 2.
|
38
|
+
spec.add_development_dependency "bundler", "~> 2.3"
|
39
39
|
spec.add_development_dependency "rake", "~> 13.0"
|
40
40
|
spec.add_development_dependency "rspec", "~> 3.10"
|
41
41
|
spec.add_development_dependency "rubocop", "~> 1.23"
|