twitchbot 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4eb24be58e5d670664da6a08c56cf3007ed3477b08c0d3e73bae57d7ee4bac29
4
+ data.tar.gz: 1ae2d56adbb8bdd41529888c3abb7d7b9f509147beddedc0fe385e54104c5b73
5
+ SHA512:
6
+ metadata.gz: a1354939320f1d974ab95107db9385e801aebf0f6b3c090d0f6a7d33f9db6c0a17bccd8e94179b6ecf8112d6f0a20623c7d3ce81a74f36c497c7170596dcdb64
7
+ data.tar.gz: ac3048f9253dcac655ec2a99787057a3ff9e3a8937af159576d11087d784b1a61d2d168fba7d701ff362630eea4294610056bf99e2eb1979bfe27263eceb9b71
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Charles Ray Shisler III
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Twitchbot
2
+
3
+ A plugin-based framework for creating Twitch chat bots, written in Ruby and based on `EventMachine` and `Faye::WebSocket`.
4
+
5
+ Notes:
6
+ * Plugins are first class citizens
7
+ * Helper functions implemented to gate-keep commands
8
+ * Bot can currently only join one channel (it might stay that way)
9
+ * Read the source until I get all the documentation completed
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'twitchbot'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install twitchbot
26
+
27
+ ## Getting Started
28
+
29
+ Require Twitchbot
30
+
31
+ require 'twitchbot'
32
+
33
+ Create Plugins
34
+
35
+ # MessagePlugin, which listens for the registered command preceeded by the Bot command_prefix
36
+ class HiPlugin
37
+ include Twitchbot::MessagePlugin
38
+
39
+ register command: 'hi', method: :say_hi
40
+
41
+ def say_hi(message, arg)
42
+ message.respond 'Hello world!'
43
+ end
44
+ end
45
+
46
+ # TimedPlugin, which fires off the registered command periodically
47
+ class ShoutOutPlugin
48
+ include Twitchbot::TimedPlugin
49
+
50
+ register method: :social, interval: 15 # Time in seconds
51
+
52
+ def social(handler)
53
+ handler.send_channel('Hello! Check my social media out!')
54
+ end
55
+ end
56
+
57
+ # Plugin, which listens for registered commands according to the raw IRC command
58
+ class PutsPlugin
59
+ include Twitchbot::Plugin
60
+
61
+ register command: 'PRIVMSG', method: :put_string
62
+
63
+ def put_string(handler)
64
+ handler.messages.each do |message|
65
+ puts message
66
+ end
67
+ end
68
+ end
69
+
70
+ Create and start `Bot`
71
+
72
+ bot = Twitchbot::Bot.new do |bot|
73
+ bot.username = 'bot_name'
74
+ bot.password = 'oauth:password'
75
+ bot.channel = 'channel_name'
76
+ bot.plugins = [HiPlugin, ShoutOutPlugin, PutsPlugin]
77
+ bot.debug = true
78
+ # bot.command_prefix = '$' # Default is '!'
79
+ end
80
+
81
+ bot.start
82
+
83
+ ## Development
84
+
85
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
86
+
87
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
88
+
89
+ ## Contributing
90
+
91
+ Bug reports and pull requests are welcome on GitHub at https://github.com/craysiii/twitchbot.
92
+
93
+ ## License
94
+
95
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'twitchbot'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+
10
+ require 'pry'
11
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/twitchbot.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'twitchbot/version'
2
+ require 'twitchbot/bot'
3
+ require 'twitchbot/plugin'
4
+ require 'twitchbot/message_plugin'
5
+ require 'twitchbot/timed_plugin'
@@ -0,0 +1,74 @@
1
+ require 'eventmachine'
2
+ require 'faye/websocket'
3
+
4
+ require_relative 'event_handler'
5
+ require_relative 'channel'
6
+ require_relative 'plugin/debug_plugin'
7
+ require_relative 'plugin/auth_plugin'
8
+ require_relative 'plugin/ping_plugin'
9
+ require_relative 'plugin/channel_plugin'
10
+ require_relative 'plugin/message_queue_plugin'
11
+
12
+ module Twitchbot
13
+ # Main Bot class that we pass all bot account information to, as well as any
14
+ # plugins that should be used
15
+ class Bot
16
+ # @return [String] Username of the bot account
17
+ attr_accessor :username
18
+ # @return [String] Password of the bot account
19
+ attr_accessor :password
20
+ # @return [Channel] The channel that the bot is connected to
21
+ attr_accessor :channel
22
+ # @return [Array] The plugins that are in use by the bot
23
+ attr_accessor :plugins
24
+ # @return [Boolean] Whether the bot should display debug info to STDOUT
25
+ attr_accessor :debug
26
+ # @return [Array] The array of messages that are to be sent to the server
27
+ attr_accessor :message_queue
28
+ # @return [String] The prefix to use when defining and parsing commands e.g. +!+
29
+ attr_accessor :command_prefix
30
+
31
+ # The connection URL for Twitch
32
+ DEFAULT_URL = 'wss://irc-ws.chat.twitch.tv'.freeze
33
+ # The built-in plugins to be used
34
+ DEFAULT_PLUGINS = [AuthPlugin,
35
+ PingPlugin,
36
+ ChannelPlugin,
37
+ MessageQueuePlugin,
38
+ DebugPlugin].freeze
39
+ # The events that eventmachine dispatches to any plugins in use
40
+ DEFAULT_EVENTS = %i[error close open message].freeze
41
+
42
+ # Create a new Bot instance, passing a block to set the necessary
43
+ # attributes to have the bot function
44
+ def initialize
45
+ @username = ''
46
+ @password = ''
47
+ @channel = ''
48
+ @plugins = []
49
+ @message_queue = Queue.new
50
+ @command_prefix = '!'
51
+
52
+ yield self
53
+
54
+ @channel = Channel.new(@channel)
55
+ end
56
+
57
+ # Start the event loop, initiate the websocket client, and register the
58
+ # plugins with eventmachine
59
+ def start
60
+ EM.run do
61
+ connection = Faye::WebSocket::Client.new DEFAULT_URL
62
+ plugins = (@plugins << DEFAULT_PLUGINS).flatten!.reverse!.map! &:new
63
+ DEFAULT_EVENTS.each do |default_event|
64
+ connection.on(default_event) do |em_event|
65
+ handler = EventHandler.new em_event, connection, self
66
+ plugins.each do |plugin|
67
+ plugin.send(default_event, handler)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,22 @@
1
+ module Twitchbot
2
+ # Class responsible for keeping track of channel attributes such as channel
3
+ # state and users in the channel
4
+ #
5
+ # TODO: Implement channel states e.g. r9k, emote-only, sub-only, slow
6
+ # TODO: Capture channel id from tags
7
+ class Channel
8
+ # @return [String] Name of the channel we have joined
9
+ attr_reader :name
10
+ # @return [Hash] Mapping of user objects according to their username
11
+ attr_accessor :users
12
+
13
+ def initialize(name)
14
+ @name = name
15
+ @users = {}
16
+ end
17
+ end
18
+
19
+ def to_s
20
+ @name
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ require_relative 'message'
2
+
3
+ module Twitchbot
4
+ # Class responsible for handling the eventmachine event that is provided
5
+ # whenever an event is fired
6
+ class EventHandler
7
+ # @return [Bot] The bot that the channel is a member of
8
+ attr_reader :bot
9
+ # @return [Array] The different messages received
10
+ attr_reader :messages
11
+ # @return [Faye::WebSocket::Client] The WebSocket client instance
12
+ attr_reader :connection
13
+
14
+ def initialize(event, connection, bot)
15
+ @connection = connection
16
+ @bot = bot
17
+
18
+ if event.respond_to? :data
19
+ @chunks = event.data.split "\n"
20
+ @messages = []
21
+ @chunks.each do |chunk|
22
+ @messages << Message.new(self, chunk.chomp)
23
+ end
24
+ end
25
+ end
26
+
27
+ # Add a raw message to the message queue
28
+ def send_raw(message)
29
+ @bot.message_queue.push(message)
30
+ end
31
+
32
+ # Add a formatted channel message to the message queue
33
+ def send_channel(message)
34
+ @bot.message_queue.push("PRIVMSG ##{bot.channel.name} :#{message}")
35
+ end
36
+
37
+ # Add a whisper to the specified user to the message queue
38
+ def send_whisper(user, message)
39
+ @bot.message_queue.push("PRIVMSG jtv :/w #{user} :#{message}")
40
+ end
41
+
42
+ # Method that provides a shortcut to grab the first message in an event
43
+ # handler. We can typically use this after authenticating, but there is no
44
+ # guarantee that twitch will not send multiple 'messages' in a single
45
+ # +:message+ event
46
+ def message
47
+ @messages.first
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,78 @@
1
+ require_relative 'user'
2
+
3
+ module Twitchbot
4
+ # Class responsible for parsing messages created in [EventHandler]
5
+ #
6
+ # TODO: Clean this up because its ugly
7
+ # TODO: Parse message-id
8
+ class Message
9
+ # @return [Integer] Number of bits that have been included in a message
10
+ attr_reader :bits
11
+ # @return [User] User that the message was sent by
12
+ attr_reader :user
13
+ # @return [Channel] Channel that the message was sent to
14
+ attr_reader :channel
15
+ # @return [String] IRC command of the raw IRC message
16
+ attr_reader :command
17
+ # @return [String] Raw IRC message received
18
+ attr_reader :raw
19
+ # @return [String] Sender of the IRC message
20
+ attr_reader :sender
21
+ # @return [String] Target of the IRC message
22
+ attr_reader :target
23
+ # @return [String] Content of the IRC message
24
+ attr_reader :payload
25
+
26
+ def initialize(handler, raw_message)
27
+ @handler = handler
28
+ @raw = raw_message
29
+ msg = @raw.dup
30
+ @tags = msg.slice! /^\S+/ if tagged?
31
+
32
+ msg.lstrip!
33
+ /^(?<sender>:\S+) (?<command>\S+)( (?<target>\S+))?( (?<payload>.+))?$/ =~ msg
34
+ @sender = sender
35
+ @command = command
36
+ @target = target
37
+ @payload = payload
38
+
39
+ if message?
40
+ @payload.slice! 0, 1
41
+ @channel = @handler.bot.channel
42
+ /bits=(?<bits>\d+)/ =~ @tags
43
+ @bits = bits.nil? ? 0 : bits.to_i
44
+ /display-name=(?<display_name>\w+)/ =~ @tags
45
+ /user-id=(?<user_id>[a-zA-Z0-9\-]+)/ =~ @tags
46
+ /badges=(?<badges>[a-zA-Z\/,0-9\-]+)/ =~ @tags
47
+ badges = badges || ''
48
+ /:(?<user>\w+)/ =~ @sender
49
+ if @channel.users.key? user
50
+ @channel.users[user].update_attributes display_name, user_id, badges
51
+ else
52
+ @channel.users[user] = User.new user, display_name, user_id, badges
53
+ end
54
+ @user = @channel.users[user]
55
+ end
56
+ end
57
+
58
+ # Method to determine if the IRC message includes any tags from the +:twitch.tv/tags+ capability
59
+ def tagged?
60
+ @raw.start_with? '@'
61
+ end
62
+
63
+ # Method to determine if the IRC message is an actual message to the [Channel] by a [User]
64
+ def message?
65
+ @command.eql? 'PRIVMSG'.freeze
66
+ end
67
+
68
+ # Method to respond to the IRC message target with a private message
69
+ def respond(message)
70
+ @handler.bot.message_queue.push("PRIVMSG #{@target} :#{message}")
71
+ end
72
+
73
+ # Method to determine if the IRC message is a PING challenge
74
+ def ping?
75
+ @raw.start_with? 'PING'
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,47 @@
1
+ module Twitchbot
2
+ module MessagePlugin
3
+
4
+ COMMANDS = {}
5
+
6
+ # Method that can be overriden to react to the eventmachine +:open+ event
7
+ def open(handler) end
8
+
9
+ def message(handler)
10
+ handler.messages.each do |message|
11
+ if message.message?
12
+ prefix = handler.bot.command_prefix
13
+ _, _command, arguments = message.payload.partition(
14
+ /#{Regexp.escape prefix}\S+/
15
+ )
16
+ command = _command.delete prefix
17
+ commands = COMMANDS[self.class]
18
+ if !command.nil? && !commands[command].nil?
19
+ send(commands[command], message, arguments)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # Method that can be overriden to react to the eventmachine +:error+ event
26
+ def error(handler) end
27
+
28
+ # Method that can be overriden to react to the eventmachine +:close+ event
29
+ def close(handler) end
30
+
31
+ # Define a class method called register on each class that includes this
32
+ # module, which allows the user to add methods to the +COMMANDS+ constant
33
+ #
34
+ # def self.register(command:, method:)
35
+ # COMMANDS[base] = {} if COMMANDS[base].nil?
36
+ # COMMANDS[base][params[:command]] = params[:method]
37
+ # end
38
+ def self.included(klass)
39
+ klass.instance_eval do
40
+ define_singleton_method 'register' do |params|
41
+ COMMANDS[klass] = {} if COMMANDS[klass].nil?
42
+ COMMANDS[klass][params[:command]] = params[:method]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,55 @@
1
+ module Twitchbot
2
+ # Base Plugin module that listens for IRC commands and calls the associated
3
+ # method of the class that includes it
4
+ module Plugin
5
+
6
+ # The commands that are registered to this base module via classes that
7
+ # include it
8
+ #
9
+ # Example state during runtime:
10
+ # { AuthPlugin: { '376': :request_caps }, ChannelPlugin: { 'CAP': :join_channel, 'JOIN': :process_join, 'PART': :process_part } }
11
+ COMMANDS = {}
12
+
13
+ # Method that can be overriden to react to the eventmachine +:open+ event
14
+ def open(handler) end
15
+
16
+ # Method that reacts to the eventmachine +:message+ event and processes each
17
+ # message in the {EventHandler}, calling the appropriate method if available.
18
+ #
19
+ # It is not recommended to override this method unless you plan on handling
20
+ # all logic for reacting to methods yourself.
21
+ def message(handler)
22
+ handler.messages.each do |message|
23
+ command = message&.command
24
+ # Grab all registered methods of the including class
25
+ commands = COMMANDS[self.class]
26
+ if !command.nil? && !commands[command].nil?
27
+ # Call the including class method and pass the EventHandler to it
28
+ send(commands[command], handler)
29
+ end
30
+ end
31
+ end
32
+
33
+ # Method that can be overriden to react to the eventmachine +:error+ event
34
+ def error(handler) end
35
+
36
+ # Method that can be overriden to react to the eventmachine +:close+ event
37
+ def close(handler) end
38
+
39
+ # Define a class method called register on each class that includes this
40
+ # module, which allows the user to add methods to the +COMMANDS+ constant
41
+ #
42
+ # def self.register(command:, method:)
43
+ # COMMANDS[base] = {} if COMMANDS[base].nil?
44
+ # COMMANDS[base][params[:command]] = params[:method]
45
+ # end
46
+ def self.included(klass)
47
+ klass.instance_eval do
48
+ define_singleton_method 'register' do |params|
49
+ COMMANDS[klass] = {} if COMMANDS[klass].nil?
50
+ COMMANDS[klass][params[:command]] = params[:method]
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ require_relative '../plugin'
2
+
3
+ module Twitchbot
4
+ # Plugin to handle authenticating the bot account and authorizing the use of
5
+ # all available Twitch IRCv3 capabilities
6
+ class AuthPlugin
7
+ include Twitchbot::Plugin
8
+
9
+ # The capabilities available to request
10
+ CAPABILITIES = %w(
11
+ twitch.tv/tags
12
+ twitch.tv/commands
13
+ twitch.tv/membership
14
+ ).freeze
15
+
16
+ # Send bot account credentials after the connection has opened
17
+ def open(handler)
18
+ handler.send_raw "PASS #{handler.bot.password}"
19
+ handler.send_raw "NICK #{handler.bot.username}"
20
+ end
21
+
22
+ register command: '376', method: :request_caps
23
+ # Listen for the last message of a successful authentication attempt and
24
+ # request capabilities
25
+ #
26
+ # > :tmi.twitch.tv 001 bot :Welcome, GLHF!
27
+ # > :tmi.twitch.tv 002 bot :Your host is tmi.twitch.tv
28
+ # > :tmi.twitch.tv 003 bot :This server is rather new
29
+ # > :tmi.twitch.tv 004 bot :-
30
+ # > :tmi.twitch.tv 375 bot :-
31
+ # > :tmi.twitch.tv 372 bot :You are in a maze of twisty passages, all alike.
32
+ # > :tmi.twitch.tv 376 bot :>
33
+ def request_caps(handler)
34
+ handler.send_raw "CAP REQ :#{CAPABILITIES.join ' '}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ require_relative '../plugin'
2
+ require_relative '../user'
3
+
4
+ module Twitchbot
5
+ # Plugin to handle joining the specified channel, as well as maintaining a
6
+ # list of users who have joined or left the channel
7
+ #
8
+ # TODO: Handle 353 and 366 (NAMES list)
9
+ class ChannelPlugin
10
+ include Twitchbot::Plugin
11
+
12
+ register command: 'CAP', method: :join_channel
13
+ # Listen for the response from AuthPlugin#request_caps and request to join
14
+ # the channel
15
+ #
16
+ # > :tmi.twitch.tv CAP * ACK :twitch.tv/tags twitch.tv/commands twitch.tv/membership
17
+ def join_channel(handler)
18
+ handler.send_raw "JOIN ##{handler.bot.channel.name}"
19
+ end
20
+
21
+ register command: 'JOIN', method: :process_join
22
+ # Listen for any JOIN commands and add the user to the channel user list
23
+ #
24
+ # > :<user>!<user>@<user>.tmi.twitch.tv JOIN #<channel>
25
+ def process_join(handler)
26
+ channel = handler.bot.channel
27
+ handler.messages.each do |message|
28
+ /:(?<sender>\w+)/ =~ message.raw
29
+ unless channel.users.key? sender
30
+ channel.users[sender] = User.new sender
31
+ end
32
+ end
33
+ end
34
+
35
+ register command: 'PART', method: :process_part
36
+ # Listen for any PART commands and remove the user from the channel user
37
+ # list
38
+ #
39
+ # > :<user>!<user>@<user>.tmi.twitch.tv PART #<channel>
40
+ def process_part(handler)
41
+ channel = handler.bot.channel
42
+ handler.messages.each do |message|
43
+ /:(?<sender>\w+)/ =~ message.raw
44
+ if channel.users.key? sender
45
+ channel.users.delete sender
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,34 @@
1
+ require_relative '../plugin'
2
+
3
+ module Twitchbot
4
+ # Plugin to handle displaying debug information such as raw messages received
5
+ # as well as connection events e.g. open, close, error
6
+ class DebugPlugin
7
+ include Twitchbot::Plugin
8
+
9
+ # Display to the user that the connection has been opened
10
+ def open(handler)
11
+ puts '! Connection established' if handler.bot.debug
12
+ end
13
+
14
+ # Display to the user any raw messages that have been received from the
15
+ # server
16
+ def message(handler)
17
+ if handler.bot.debug
18
+ handler.messages.each do |line|
19
+ puts "> #{line.raw}"
20
+ end
21
+ end
22
+ end
23
+
24
+ # Display to the user that the connection has encountered an error
25
+ def error(handler)
26
+ puts '! Error occurred' if handler.bot.debug
27
+ end
28
+
29
+ # Display to the user that the connection has been closed
30
+ def close(handler)
31
+ puts '! Connection closed' if handler.bot.debug
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../timed_plugin'
2
+
3
+ module Twitchbot
4
+ # Plugin to handle sending messages to the server that have been queued from
5
+ # any plugins running.
6
+ #
7
+ # TODO: Implement different levels of rate limiting according to bot status e.g. :mod, :verified, :trusted
8
+ class MessageQueuePlugin
9
+ include Twitchbot::TimedPlugin
10
+
11
+ # The most privileged bot can only send 7200 messages every 30 seconds
12
+ register method: :send_message, interval: (30 / 7200)
13
+ # Pull a message from the message queue if any are available and send to the
14
+ # server
15
+ def send_message(handler)
16
+ queue = handler.bot.message_queue
17
+ unless queue.empty?
18
+ message = queue.pop
19
+ puts "< #{message}" if handler.bot.debug
20
+ handler.connection.send message
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ require_relative '../plugin'
2
+
3
+ module Twitchbot
4
+ # Plugin to handle any PING messages from the server and keep the connection
5
+ # alive
6
+ class PingPlugin
7
+ include Twitchbot::Plugin
8
+
9
+ # Listen for any PING messages and respond with PONG
10
+ #
11
+ # > PING :tmi.twitch.tv
12
+ def message(handler)
13
+ handler.send_raw('PONG :tmi.twitch.tv') if handler.message.ping?
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ require 'eventmachine'
2
+
3
+ module Twitchbot
4
+ # Base Plugin module that registers methods that should be called periodically
5
+ module TimedPlugin
6
+
7
+ COMMANDS = {}
8
+
9
+ # Method that reacts to the eventmachine +:open+ event and creates a new
10
+ # eventmachine periodic timer for each plugin of this type
11
+ #
12
+ # It is not recommended to override this method unless you plan on handling
13
+ # all logic for managing timed plugins yourself
14
+ def open(handler)
15
+ COMMANDS[self.class].each do |command, interval|
16
+ EM::PeriodicTimer.new(interval) do
17
+ send(command, handler)
18
+ end
19
+ end
20
+ end
21
+
22
+ # Method that can be overriden to react to the eventmachine +:message+ event
23
+ def message(event) end
24
+
25
+ # Method that can be overriden to react to the eventmachine +:error+ event
26
+ def error(event) end
27
+
28
+ # Method that can be overriden to react to the eventmachine +:close+ event
29
+ def close(event) end
30
+
31
+ # Define a class method called register on each class that includes this
32
+ # module, which allows the user to add methods to the +COMMANDS+ constant
33
+ #
34
+ # def self.register(command:, method:)
35
+ # COMMANDS[base] = {} if COMMANDS[base].nil?
36
+ # COMMANDS[base][params[:command]] = params[:method]
37
+ # end
38
+ def self.included(klass)
39
+ klass.instance_eval do
40
+ define_singleton_method 'register' do |params|
41
+ COMMANDS[klass] = {} if COMMANDS[klass].nil?
42
+ COMMANDS[klass][params[:method]] = params[:interval]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,80 @@
1
+ module Twitchbot
2
+ # Class responsible for keeping track of a user attributes such as their
3
+ # display name, id, any badges they have, and implementing helper functions
4
+ # to determine if different qualities of the user
5
+ class User
6
+ # @return [String] Server ID for the user
7
+ attr_reader :id
8
+ # @return [Hash] Collection of known badges for the user
9
+ attr_reader :badges
10
+
11
+ def initialize(name, display_name = nil, id = nil, badges = nil)
12
+ @name = name
13
+ @display_name = display_name
14
+ @id = id
15
+ @badges = badges.nil? ? nil : process_badges(badges)
16
+ end
17
+
18
+ # Method to update the main attributes of a user
19
+ def update_attributes(display_name, id, badges)
20
+ @display_name = display_name
21
+ @user_id = id
22
+ @badges = process_badges(badges)
23
+ end
24
+
25
+ # Method to grab the best representation of a user
26
+ def name
27
+ @display_name || @name
28
+ end
29
+
30
+ # Method to determine if the user is a moderator of the channel
31
+ def mod?
32
+ @badges.key? 'moderator'
33
+ end
34
+
35
+ # Method to determine if the user is a subscriber to the channel
36
+ def sub?
37
+ @badges.key? 'subscriber'
38
+ end
39
+
40
+ # Method to determine if the user has Twitch Prime
41
+ def prime?
42
+ @badges.key? 'premium'
43
+ end
44
+
45
+ # Method to determine if the user has ever donated to the channel
46
+ def donator?
47
+ @badges.key? 'bits'
48
+ end
49
+
50
+ # Method to determine if the user is a channel founder
51
+ def founder?
52
+ @badges.key? 'founder'
53
+ end
54
+
55
+ # Method to determine if the user is on the leaderboard for subscriber gifts
56
+ def sub_gift_leader?
57
+ @badges.key? 'sub-gift-leader'
58
+ end
59
+
60
+ # Method to determine if the user is on the leaderboard for bit gifts
61
+ def bits_leader?
62
+ @badges.key? 'bits-leader'
63
+ end
64
+
65
+ # Method to process the string representation of badges into a Hash so that
66
+ # we can query it for specific badges and levels of the badges
67
+ def process_badges(badges)
68
+ badge = {}
69
+ badges.split(',').each do |_badge|
70
+ type, value = _badge.split '/'
71
+ badge[type] = value.to_i
72
+ end
73
+ badge
74
+ end
75
+
76
+ def to_s
77
+ name
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,3 @@
1
+ module Twitchbot
2
+ VERSION = '0.0.2'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twitchbot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Charles Ray Shisler III
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-11-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faye-websocket
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.10.7
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.10.7
83
+ description:
84
+ email:
85
+ - charles@cray.io
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE.txt
91
+ - README.md
92
+ - bin/console
93
+ - bin/setup
94
+ - lib/twitchbot.rb
95
+ - lib/twitchbot/bot.rb
96
+ - lib/twitchbot/channel.rb
97
+ - lib/twitchbot/event_handler.rb
98
+ - lib/twitchbot/message.rb
99
+ - lib/twitchbot/message_plugin.rb
100
+ - lib/twitchbot/plugin.rb
101
+ - lib/twitchbot/plugin/auth_plugin.rb
102
+ - lib/twitchbot/plugin/channel_plugin.rb
103
+ - lib/twitchbot/plugin/debug_plugin.rb
104
+ - lib/twitchbot/plugin/message_queue_plugin.rb
105
+ - lib/twitchbot/plugin/ping_plugin.rb
106
+ - lib/twitchbot/timed_plugin.rb
107
+ - lib/twitchbot/user.rb
108
+ - lib/twitchbot/version.rb
109
+ homepage: https://github.com/craysiii/twitchbot
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 3.0.3
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: A framework for creating Twitch.tv chat bots
132
+ test_files: []