lobstersbot 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69f8315be206c88998ff12791d13b5fcce6a947b51eed928be0811f14a65700b
4
- data.tar.gz: afae0e599f4a71512dc750baad1a84c7fb9c17807a10a680bdaaf7e86eb3f3f7
3
+ metadata.gz: 68c53d90cc65d235b50060bdf775931f8e72048c7a38e5ea26b1e91431126b6c
4
+ data.tar.gz: 2cdda10f35bf5692f5941926473737e1fccf69db756c32307cbb5ac0b9873253
5
5
  SHA512:
6
- metadata.gz: 70d84d584628b2a6a6d37218a77b0ac95a5a072f17fe5f911a4ac6a485b897d7e14e25600f4ddbd4668a2e7967260ddb389ce6d53a16166ca5d216ca7889aad8
7
- data.tar.gz: 2491273ecb1a31c4950cf6852b45c94ca4cb0b9570242e2502f7b7aac1c2bf546e17cf08a607e8d5f2908d528475c49e7ff3b0d7625cc92ef84fb9705893e4d4
6
+ metadata.gz: ce6a93ce8dae08db804359901291248207428ad74c5c66555cf49f62281427e235cd24431b609265446a75965e35942d07e52af67abcd52ff3c02759a3844016
7
+ data.tar.gz: 500edebf6ce3e758fe2e0a53d1ab501de5319952b8dfa107cbe96b0f19e8c8535ef7f6e5958ec809e5bbe202c0b3cacfbc2cad38f7d3414998ab69a2234d6b67
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: Gem
3
+ on: push
4
+
5
+ jobs:
6
+ rspec:
7
+ name: Rspec
8
+ runs-on: ubuntu-latest
9
+
10
+ steps:
11
+ - name: Checkout code
12
+ uses: actions/checkout@v2
13
+ - name: Setup Ruby
14
+ uses: actions/setup-ruby@v1
15
+ with:
16
+ ruby-version: '2.6'
17
+ - name: Run Suite
18
+ run: |
19
+ gem install bundler
20
+ bundle install --jobs 4 --retry 3
21
+ bundle exec rspec
22
+ rubocop:
23
+ name: Rubocop
24
+ runs-on: ubuntu-latest
25
+
26
+ steps:
27
+ - name: Checkout code
28
+ uses: actions/checkout@v2
29
+ - name: Setup Ruby
30
+ uses: actions/setup-ruby@v1
31
+ with:
32
+ ruby-version: '2.6'
33
+ - name: Run Rubocop
34
+ run: |
35
+ gem install bundler
36
+ bundle install --jobs 4 --retry 3
37
+ bundle exec rubocop
@@ -0,0 +1,253 @@
1
+ # Please do not 'fix' style issues without a compelling, metrics-driven
2
+ # argument that a style change will materially improve cod equality.
3
+ # https://github.com/lobsters/lobsters/pull/460
4
+
5
+ # Project setup:
6
+ require:
7
+ - rubocop-rspec
8
+ AllCops:
9
+ Include:
10
+ - '**/*.rb'
11
+ - '**/Rakefile'
12
+ - '**/config.ru'
13
+ - '**/*.rake'
14
+ Exclude:
15
+ - Gemfile
16
+ - 'bin/**/*'
17
+ - 'db/**/*'
18
+ - 'vendor/**/*'
19
+
20
+ # Cop configuration:
21
+
22
+ # Bundler
23
+
24
+ # Gemspec
25
+
26
+ # Layout
27
+ Layout/AccessModifierIndentation:
28
+ EnforcedStyle: outdent
29
+ Layout/ArrayAlignment:
30
+ Enabled: false
31
+ Layout/CaseIndentation:
32
+ EnforcedStyle: end
33
+ Layout/ElseAlignment:
34
+ Enabled: false
35
+ Layout/EmptyLineAfterGuardClause:
36
+ Enabled: false
37
+ Layout/EmptyLinesAroundExceptionHandlingKeywords:
38
+ Enabled: false
39
+ Layout/EndAlignment:
40
+ EnforcedStyleAlignWith: variable
41
+ Layout/FirstHashElementIndentation:
42
+ EnforcedStyle: consistent
43
+ Layout/HashAlignment:
44
+ Enabled: false
45
+ Layout/LineLength:
46
+ Max: 100
47
+ Layout/MultilineMethodCallBraceLayout:
48
+ Enabled: false
49
+ Layout/MultilineMethodCallIndentation:
50
+ Enabled: false
51
+ Layout/SpaceAroundOperators:
52
+ Enabled: false
53
+ Layout/SpaceInsideBlockBraces:
54
+ EnforcedStyle: space
55
+ SpaceBeforeBlockParameters: false
56
+ Layout/SpaceInsideRangeLiteral:
57
+ Enabled: false
58
+
59
+ # Lint
60
+ Lint/SuppressedException:
61
+ Enabled: false
62
+ Lint/RaiseException:
63
+ Enabled: true
64
+ Lint/StructNewOverride:
65
+ Enabled: true
66
+
67
+ # Metrics
68
+ Metrics/AbcSize:
69
+ Enabled: false
70
+ Metrics/BlockLength:
71
+ Enabled: false
72
+ Metrics/BlockNesting:
73
+ Enabled: false
74
+ Metrics/ClassLength:
75
+ Enabled: false
76
+ Metrics/CyclomaticComplexity:
77
+ Enabled: false
78
+ Metrics/MethodLength:
79
+ Enabled: false
80
+ Metrics/ModuleLength:
81
+ Enabled: false
82
+ Metrics/ParameterLists:
83
+ Enabled: false
84
+ Metrics/PerceivedComplexity:
85
+ Enabled: false
86
+
87
+ # Naming
88
+ Naming/AccessorMethodName:
89
+ Enabled: false
90
+ Naming/MemoizedInstanceVariableName:
91
+ Enabled: false
92
+ Naming/PredicateName:
93
+ Enabled: false
94
+ # disabled until class vaiables in extras become constants
95
+ Naming/MethodParameterName:
96
+ Enabled: false
97
+ Naming/VariableName:
98
+ Enabled: false
99
+
100
+ # Performance
101
+
102
+ # RSpec
103
+ RSpec/ExampleLength:
104
+ Enabled: false
105
+ RSpec/MultipleExpectations:
106
+ Enabled: false
107
+ RSpec/DescribedClass:
108
+ Enabled: false
109
+ RSpec/MessageSpies:
110
+ Enabled: false
111
+ RSpec/VerifiedDoubles:
112
+ Enabled: false
113
+ RSpec/NotToNot:
114
+ Enabled: false
115
+ RSpec/ContextWording:
116
+ Enabled: false
117
+ RSpec/ExpectChange:
118
+ Enabled: false
119
+ RSpec/HookArgument:
120
+ Enabled: false
121
+ RSpec/ExampleWording:
122
+ Enabled: false
123
+ RSpec/NamedSubject:
124
+ Enabled: false
125
+ RSpec/NestedGroups:
126
+ Enabled: false
127
+ RSpec/AnyInstance:
128
+ Enabled: false
129
+ RSpec/UnspecifiedException:
130
+ Enabled: false
131
+ RSpec/InstanceVariable:
132
+ Enabled: false
133
+ RSpec/BeforeAfterAll:
134
+ Enabled: false
135
+ RSpec/DescribeClass:
136
+ Enabled: false
137
+ Capybara/FeatureMethods:
138
+ Enabled: false
139
+
140
+ # Security
141
+
142
+ # Style
143
+ Style/AndOr:
144
+ EnforcedStyle: conditionals
145
+ Style/BlockDelimiters:
146
+ Enabled: false
147
+ Style/BlockComments:
148
+ Enabled: false
149
+ Style/CommentedKeyword:
150
+ Enabled: false
151
+ Style/ClassVars:
152
+ Enabled: false
153
+ Style/ConditionalAssignment:
154
+ Enabled: false
155
+ # It would be nice to have this on, but I'm not up for writing 66 of these in
156
+ # the process of addressing the initial 4,489 cop warnings.
157
+ Style/Documentation:
158
+ Enabled: false
159
+ Exclude: [db/migrate/**/*, spec/**/*, test/**/*]
160
+ Style/DoubleNegation:
161
+ Enabled: false
162
+ Style/EachWithObject:
163
+ Enabled: false
164
+ Style/ExpandPathArguments:
165
+ Enabled: false
166
+ Style/FormatString:
167
+ Enabled: false
168
+ Style/FormatStringToken:
169
+ Enabled: false
170
+ Style/FrozenStringLiteralComment:
171
+ EnforcedStyle: never
172
+ Style/GuardClause:
173
+ Enabled: false
174
+ Style/HashEachMethods:
175
+ Enabled: true
176
+ Style/HashSyntax:
177
+ EnforcedStyle: no_mixed_keys
178
+ Style/HashTransformKeys:
179
+ Enabled: true
180
+ Style/HashTransformValues:
181
+ Enabled: true
182
+ Style/IfInsideElse:
183
+ Enabled: false
184
+ Style/IfUnlessModifier:
185
+ Enabled: false
186
+ Style/InverseMethods:
187
+ Enabled: false
188
+ Style/Lambda:
189
+ Enabled: false
190
+ Style/LineEndConcatenation:
191
+ Enabled: false
192
+ Style/MethodDefParentheses:
193
+ Enabled: false
194
+ Style/MultilineBlockChain:
195
+ Enabled: false
196
+ Style/MultilineTernaryOperator:
197
+ Enabled: false
198
+ Style/NegatedIf:
199
+ EnforcedStyle: postfix
200
+ Style/NegatedWhile:
201
+ Enabled: false
202
+ Style/Next:
203
+ Enabled: false
204
+ Style/NonNilCheck:
205
+ Enabled: false
206
+ Style/NumericPredicate:
207
+ Enabled: false
208
+ Style/ParallelAssignment:
209
+ Enabled: false
210
+ Style/PercentLiteralDelimiters:
211
+ Enabled: false
212
+ Style/PerlBackrefs:
213
+ Enabled: false
214
+ Style/RaiseArgs:
215
+ EnforcedStyle: compact
216
+ Style/RedundantBegin:
217
+ Enabled: false
218
+ Style/RedundantParentheses:
219
+ Enabled: false
220
+ Style/RedundantReturn:
221
+ Enabled: false
222
+ Style/RedundantSelf:
223
+ Enabled: false
224
+ Style/RescueModifier:
225
+ Enabled: false
226
+ Style/RescueStandardError:
227
+ EnforcedStyle: implicit
228
+ Style/RegexpLiteral:
229
+ AllowInnerSlashes: true
230
+ Style/SafeNavigation:
231
+ Enabled: false
232
+ Style/SignalException:
233
+ Enabled: false
234
+ Style/StringLiterals:
235
+ Enabled: false
236
+ Style/SymbolArray:
237
+ EnforcedStyle: brackets
238
+ Style/TrailingCommaInArguments:
239
+ Enabled: false
240
+ Style/TrailingCommaInArrayLiteral:
241
+ EnforcedStyleForMultiline: consistent_comma
242
+ Style/TrailingCommaInHashLiteral:
243
+ EnforcedStyleForMultiline: consistent_comma
244
+ Style/WhileUntilDo:
245
+ Enabled: false
246
+ Style/WhileUntilModifier:
247
+ Enabled: false
248
+ Style/WordArray:
249
+ Enabled: false
250
+ Style/YodaCondition:
251
+ Enabled: false
252
+ Style/ZeroLengthPredicate:
253
+ Enabled: false
@@ -1,12 +1,14 @@
1
- #!/usr/local/bin/ruby -w
1
+ #!/usr/bin/env -S ruby -w
2
2
 
3
3
  require 'lobstersbot'
4
4
 
5
5
  class Bot < Summer::Connection
6
+ include Lobstersbot::SummerPatches
6
7
  include Lobstersbot::PluggableConnection
7
-
8
+
8
9
  include Lobstersbot::Tell
9
10
  include Lobstersbot::Rss
11
+ include Lobstersbot::Salute
10
12
  end
11
13
 
12
14
  Bot.new("irc.freenode.net")
@@ -7,10 +7,12 @@ require 'timers'
7
7
 
8
8
  require 'lobstersbot/version'
9
9
  require 'lobstersbot/rss_reader'
10
+ require 'lobstersbot/summer_patches'
10
11
  require 'lobstersbot/pluggable_connection'
11
12
 
12
13
  require 'lobstersbot/plugins/tell'
13
14
  require 'lobstersbot/plugins/rss'
15
+ require 'lobstersbot/plugins/salute'
14
16
 
15
17
  module Lobstersbot
16
18
  class Error < StandardError; end
@@ -1,45 +1,73 @@
1
1
  module Lobstersbot
2
2
  module PluggableConnection
3
- def config_dir(file)
4
- File.join(ARGV[0], file)
3
+ @@triggers = [
4
+ # The default command handler.
5
+ [
6
+ 0,
7
+ /\A\.(?<command>[a-z]+)\s(?<message>.+)\z/i,
8
+ lambda do |bot, channel, nick, match|
9
+ bot.evaluate(:"on_#{match[:command]}", channel, nick, match[:message])
10
+ end,
11
+ ],
12
+ ]
13
+
14
+ def self.triggers
15
+ @@triggers
5
16
  end
6
-
7
- def load_config
8
- @config = HashWithIndifferentAccess.new(YAML.load_file(config_dir('lobstersbot.yml')))
17
+
18
+ def self.included(mod)
19
+ mod.extend(ClassMethods)
9
20
  end
10
-
21
+
22
+ module ClassMethods
23
+ def add_trigger(priority, regex, handler)
24
+ PluggableConnection.triggers << [priority, regex, handler]
25
+ end
26
+ end
27
+
28
+ def respond(channel, nick, msg)
29
+ privmsg("#{nick}: #{msg}", channel)
30
+ end
31
+
11
32
  def did_start_up
12
33
  pp ARGV
13
34
  @timers = Timers::Group.new
14
35
  @memory = PStore.new(config_dir('memory.pstore'), true)
15
-
36
+
37
+ # Sort the triggers by priority.
38
+ @@triggers.sort! {|a, b| b[0] <=> a[0] }
39
+
16
40
  @timers.every(60) { evaluate(:frequently) }
17
41
  Thread.new { loop { @timers.wait } }
18
42
  end
19
-
43
+
20
44
  def channel_message(sender, channel, message)
21
- request = message.match(/\A\.(?<command>[a-z]+)\s(?<message>.+)\z/i)
22
- return unless request
23
- response_proc = ->(msg) { privmsg("#{sender[:nick]}: #{msg}", channel) }
24
-
25
- evaluate(:"on_#{request[:command]}", sender[:nick], request[:message], response_proc)
45
+ match = nil
46
+
47
+ _, _, handler = @@triggers.find do |trigger|
48
+ match = message.match(trigger[1])
49
+ !match.nil?
50
+ end
51
+ return unless match
52
+
53
+ handler.call(self, channel, sender[:nick], match)
26
54
  end
27
-
55
+
28
56
  def join_event(sender, channel)
29
57
  response_proc = ->(msg) { privmsg("#{sender[:nick]}: #{msg}", channel) }
30
58
  evaluate(:seen, sender[:nick], response_proc)
31
59
  end
32
-
60
+
33
61
  def evaluate(group, *args)
34
- matching = public_methods.select { |m| m.to_s.start_with?(group.to_s) }
35
-
62
+ matching = public_methods.select {|m| m.to_s.start_with?(group.to_s) }
63
+
36
64
  matching.each do |match|
37
65
  slice_name = match.to_s.sub("#{group}_", '').to_sym
38
66
  @memory.transaction do
39
- slice = @memory[slice_name] ||= Hash.new
67
+ slice = @memory[slice_name] ||= {}
40
68
  method(match).call(slice, *args)
41
69
  end
42
70
  end
43
71
  end
44
72
  end
45
- end
73
+ end
@@ -1,13 +1,13 @@
1
1
  module Lobstersbot
2
2
  module Rss
3
3
  def frequently_post_stories(
4
- memory,
4
+ memory,
5
5
  reader = RssReader.new('https://lobste.rs/newest.rss')
6
6
  )
7
7
  reader.call(memory[:last_run]).each do |story|
8
- @config[:channels].each { |c| privmsg(story, c) }
8
+ @config[:channels].each {|c| privmsg(story, c) }
9
9
  end
10
10
  memory[:last_run] = Time.now.to_i
11
11
  end
12
12
  end
13
- end
13
+ end
@@ -0,0 +1,18 @@
1
+ module Lobstersbot
2
+ module Salute
3
+ @@claws = ['(V)', '(\\/)', '(\\_/)', 'V', 'v', '(v)']
4
+ @@faces = ['_!_!_', '.v.', '_00_']
5
+
6
+ def salute_user(channel)
7
+ claws = @@claws.sample 2
8
+ face = @@faces.sample
9
+ lobster = claws[0] + face + claws[1]
10
+ privmsg(lobster, channel)
11
+ end
12
+
13
+ def self.included(mod)
14
+ handler = ->(bot, channel, _nick, _match) { bot.salute_user channel }
15
+ mod.add_trigger(100, /\AV.v.V\z/i, handler)
16
+ end
17
+ end
18
+ end
@@ -1,19 +1,19 @@
1
1
  module Lobstersbot
2
2
  module Tell
3
- def on_tell(memory, nick, message, response)
3
+ def on_tell(memory, channel, nick, message)
4
4
  parsed = message.match(/(?<target>[^\s]+)\s(?<message>.+)/)
5
5
  return unless parsed
6
-
6
+
7
7
  target = parsed[:target]
8
- memory[target] ||= Array.new
8
+ memory[target] ||= []
9
9
  memory[target] << "#{nick}: #{parsed[:message]}"
10
-
11
- response.call("I'll pass that along when #{target} is around.")
10
+
11
+ respond(channel, nick, "I'll pass that along when #{target} is around.")
12
12
  end
13
-
13
+
14
14
  def seen_tell(memory, nick, response)
15
15
  return unless memory[nick].is_a? Array
16
- memory[nick].each { |msg| response.call(msg) }
16
+ memory[nick].each {|msg| response.call(msg) }
17
17
  end
18
18
  end
19
- end
19
+ end
@@ -3,31 +3,31 @@ require 'rss'
3
3
 
4
4
  module Lobstersbot
5
5
  class RssReader
6
- FORMAT = '%s %s (%s) - %s'
7
- USERNAME_REGXP = /\((?<username>.+)\)/i
8
-
6
+ FORMAT = '%s %s (%s) - %s'.freeze
7
+ USERNAME_REGXP = /\((?<username>.+)\)/i # rubocop:disable Style/MutableConstant
8
+
9
9
  def initialize(endpoint, open_proc = URI.method(:open))
10
10
  @endpoint = endpoint
11
11
  @open = open_proc
12
12
  end
13
-
13
+
14
14
  def call(not_before)
15
15
  feed = @open.call(@endpoint)
16
16
  rss = RSS::Parser.parse(feed)
17
-
17
+
18
18
  rss.items.map do |item|
19
19
  item.pubDate.to_i > not_before ? format_item(item) : nil
20
20
  end.compact
21
21
  end
22
-
23
- private
24
-
22
+
23
+ private
24
+
25
25
  def format_item(item)
26
26
  id = item.guid.content
27
- categories = item.categories.map(&:content).map { |n| "[%s]" % n}
27
+ categories = item.categories.map(&:content).map {|n| "[%s]" % n }
28
28
  username = item.author.match(USERNAME_REGXP)[:username]
29
-
29
+
30
30
  FORMAT % [item.title, categories.join(' '), username, id]
31
31
  end
32
32
  end
33
- end
33
+ end
@@ -0,0 +1,27 @@
1
+ module Lobstersbot
2
+ module SummerPatches
3
+ def config_dir(file)
4
+ File.join(ARGV[0], file)
5
+ end
6
+
7
+ def load_config
8
+ @config = HashWithIndifferentAccess.new(YAML.load_file(config_dir('lobstersbot.yml')))
9
+ end
10
+
11
+ def connect!
12
+ @connection = TCPSocket.open(server, port)
13
+
14
+ if config[:use_ssl]
15
+ cert_file = File.read(config_dir('client.pem'))
16
+ context = OpenSSL::SSL::SSLContext.new
17
+ context.key = OpenSSL::PKey.read(cert_file)
18
+ context.crt = OpenSSL::X509::Certificate.new(cert_file)
19
+
20
+ @connection = OpenSSL::SSL::SSLSocket.new(@connection, context).connect
21
+ end
22
+
23
+ response("USER #{config[:nick]} #{config[:nick]} #{config[:nick]} #{config[:nick]}")
24
+ response("NICK #{config[:nick]}")
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module Lobstersbot
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0".freeze
3
3
  end
@@ -27,6 +27,9 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "rake", "~> 10.0"
28
28
  spec.add_development_dependency "rspec", "~> 3.0"
29
29
 
30
+ spec.add_development_dependency "rubocop", "~> 1.8"
31
+ spec.add_development_dependency "rubocop-rspec", "~> 2.1.0"
32
+
30
33
  spec.add_runtime_dependency 'summer', '~> 1.0'
31
34
  spec.add_runtime_dependency 'timers', '~> 4.1'
32
35
  spec.add_runtime_dependency 'rss', '~> 0.2.9'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lobstersbot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hunter Madison
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-17 00:00:00.000000000 Z
11
+ date: 2021-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 2.1.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 2.1.0
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: summer
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -102,8 +130,10 @@ executables:
102
130
  extensions: []
103
131
  extra_rdoc_files: []
104
132
  files:
133
+ - ".github/workflows/gem.yml"
105
134
  - ".gitignore"
106
135
  - ".rspec"
136
+ - ".rubocop.yml"
107
137
  - ".travis.yml"
108
138
  - Gemfile
109
139
  - LICENSE.txt
@@ -115,8 +145,10 @@ files:
115
145
  - lib/lobstersbot.rb
116
146
  - lib/lobstersbot/pluggable_connection.rb
117
147
  - lib/lobstersbot/plugins/rss.rb
148
+ - lib/lobstersbot/plugins/salute.rb
118
149
  - lib/lobstersbot/plugins/tell.rb
119
150
  - lib/lobstersbot/rss_reader.rb
151
+ - lib/lobstersbot/summer_patches.rb
120
152
  - lib/lobstersbot/version.rb
121
153
  - lobstersbot.gemspec
122
154
  homepage: https://github.com/lobsters/lobstersbot