twitch_plays 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 59c3c777cdfd0a165cb02b9989c99e11fc393235
4
- data.tar.gz: 0fe695162142a9206d2c704d2ddef4b1fdcc7ca9
3
+ metadata.gz: 7f5eff287aae3d3f6221c28b55737e048175a81c
4
+ data.tar.gz: 599ed21f42926111ac564d491bb9ebbab2a781f5
5
5
  SHA512:
6
- metadata.gz: 6d23154385b836e69d92fa9a912226fc3733e69488c402ed113b3c55e7909fb852afc728396d4e7ffb08973a467cd18be53ea26842b430e67554479d412ad305
7
- data.tar.gz: c4ed7029795388bb91df1422b137d7694093327695213b4036264895500136625893dd93d02723d8e2bd7259002acd4306e1a89850370e46ba791e4a1ab416e3
6
+ metadata.gz: 8db97deb045cbe09dc0e1eecea0b0747034d1c0eec1f2d86b505d86a91649454b25a12a24e2ddd4c1875bbffc9cbe168b35c01faf510b1ab24cce1ae6c24b0f8
7
+ data.tar.gz: 80e19829f0c751bb97d2094cbc1fae45c8df52fbbaf4cfff29f2f9a1976dc69792f96ce01fc6129a36a98c21b89f6a5670afbffa1fd13aa108f7fa5e1aaa2cd2
data/README.md CHANGED
@@ -3,6 +3,12 @@ twitch_plays
3
3
 
4
4
  Yet another Twitch Plays Pokemon clone. MMO-ify any game over IRC.
5
5
 
6
+ Prerequisites
7
+ -------------
8
+
9
+ Requires xdotool on Linux.
10
+
11
+
6
12
  Configuration
7
13
  -------------
8
14
 
data/config.yml CHANGED
@@ -1,11 +1,13 @@
1
1
  # CONNECTION INFO
2
- :nick: TwitchPlaysSomething # same as Twitch username
3
- :password: # get one at http://twitchapps.com/tmi/
2
+ :nick: twitchplaysbot # Twitch username in all lowercase
3
+ :password: # Get one at http://twitchapps.com/tmi/
4
4
  :server: irc.twitch.tv
5
5
  :port: 6667
6
- :channel: '#TwitchPlaysSomething' # Twitch channel to listen on
6
+ :channel: '#twitchplaysbot' # Twitch channel to listen on, all lowercase
7
7
 
8
8
  :log_line_length: 35 # Enough to fit a twitch username, a space, and "democracy"
9
+ :use_touch: true
10
+ :touch_move_amount: 50 # How many pixels to move the mouse when a touchscreen move is performed
9
11
 
10
12
  # KEYMAP
11
13
  # key names are as in X's keysymdef.h, without the leading XK_
@@ -15,14 +17,14 @@
15
17
  :down: Down
16
18
  :left: Left
17
19
  :right: Right
18
- :a: c
19
- :b: x
20
+ :a: d
21
+ :b: c
20
22
  :x: s
21
23
  :y: x
22
24
  :l: z
23
25
  :r: a
24
26
  :start: Return
25
- :select: space
27
+ :select: Tab
26
28
  :save_state: F1
27
29
 
28
30
  # DEMOCRACY
@@ -0,0 +1,151 @@
1
+ require 'ffi'
2
+
3
+ module TwitchPlays
4
+ module OS
5
+ module Win
6
+ module User32
7
+ extend FFI::Library
8
+
9
+ INPUT_MOUSE = 0x00
10
+ INPUT_KEYBOARD = 0x01
11
+
12
+ MOUSEEVENTF_MOVE = 0x01
13
+ MOUSEEVENTF_LEFTDOWN = 0x02
14
+ MOUSEEVENTF_LEFTUP = 0x04
15
+
16
+ VK_BACK = 0x08
17
+ VK_TAB = 0x09
18
+ VK_RETURN = 0x0d
19
+ VK_SHIFT = 0x10
20
+ VK_CONTROL = 0x11
21
+ VK_SPACE = 0x20
22
+ VK_LEFT = 0x25
23
+ VK_UP = 0x26
24
+ VK_RIGHT = 0x27
25
+ VK_DOWN = 0x28
26
+ VK_F1 = 0x70
27
+ VK_F2 = 0x71
28
+ VK_F3 = 0x72
29
+ VK_F4 = 0x73
30
+ VK_F5 = 0x74
31
+ VK_F6 = 0x75
32
+ VK_F7 = 0x76
33
+ VK_F8 = 0x77
34
+ VK_F9 = 0x78
35
+ VK_F10 = 0x79
36
+ VK_F11 = 0x7a
37
+ VK_F12 = 0x7b
38
+
39
+ class MouseInput < FFI::Struct
40
+ layout(:dx, :long,
41
+ :dy, :long,
42
+ :mouse_data, :uint,
43
+ :flags, :uint,
44
+ :time, :uint,
45
+ :extra_info, :pointer)
46
+ end
47
+
48
+ class KeyboardInput < FFI::Struct
49
+ layout(:vk, :ushort,
50
+ :scan, :ushort,
51
+ :flags, :uint,
52
+ :time, :uint,
53
+ :extra_info, :pointer)
54
+ end
55
+
56
+ class InputEvent < FFI::Union
57
+ layout(:mi, MouseInput,
58
+ :ki, KeyboardInput)
59
+ end
60
+
61
+ class Input < FFI::Struct
62
+ layout(:type, :uint,
63
+ :evt, InputEvent)
64
+ end
65
+
66
+ ffi_lib 'user32'
67
+ ffi_convention :stdcall
68
+
69
+ attach_function :SendInput, [:uint, :pointer, :int], :uint
70
+ end
71
+
72
+ TRANSLATE_KEYS = {
73
+ 'BackSpace' => User32::VK_BACK,
74
+ 'Tab' => User32::VK_TAB,
75
+ 'Return' => User32::VK_RETURN,
76
+ 'Shift_L' => User32::VK_SHIFT,
77
+ 'Shift_R' => User32::VK_SHIFT,
78
+ 'Control_L' => User32::VK_CONTROL,
79
+ 'Control_R' => User32::VK_CONTROL,
80
+ 'space' => User32::VK_SPACE,
81
+ 'Left' => User32::VK_LEFT,
82
+ 'Up' => User32::VK_UP,
83
+ 'Right' => User32::VK_RIGHT,
84
+ 'Down' => User32::VK_DOWN,
85
+ 'F1' => User32::VK_F1,
86
+ 'F2' => User32::VK_F2,
87
+ 'F3' => User32::VK_F3,
88
+ 'F4' => User32::VK_F4,
89
+ 'F5' => User32::VK_F5,
90
+ 'F6' => User32::VK_F6,
91
+ 'F7' => User32::VK_F7,
92
+ 'F8' => User32::VK_F8,
93
+ 'F9' => User32::VK_F9,
94
+ 'F10' => User32::VK_F10,
95
+ 'F11' => User32::VK_F11,
96
+ 'F12' => User32::VK_F12
97
+ }
98
+
99
+ def self.press(key)
100
+ input = User32::Input.new
101
+ input[:type] = User32::INPUT_KEYBOARD
102
+ evt = input[:evt][:ki]
103
+ evt[:vk] = TRANSLATE_KEYS[key] || key.upcase.ord
104
+ evt[:scan] = 0
105
+ evt[:flags] = 0
106
+ evt[:time] = 0
107
+ evt[:extra_info] = 0
108
+ User32.SendInput(1, [input], User32::Input.size)
109
+ end
110
+
111
+ def self.touch_move(dx, dy)
112
+ input = User32::Input.new
113
+ input[:type] = User32::INPUT_MOUSE
114
+ evt = input[:evt][:mi]
115
+ evt[:dx] = dx
116
+ evt[:dy] = dy
117
+ evt[:mouse_data] = 0
118
+ evt[:flags] = User32::MOUSEEVENTF_MOVE
119
+ evt[:time] = 0
120
+ evt[:extra_info] = 0
121
+ User32.SendInput(1, [input], User32::Input.size)
122
+ end
123
+
124
+ def self.touch_press
125
+ input = User32::Input.new
126
+ input[:type] = User32::INPUT_MOUSE
127
+ evt = input[:evt][:mi]
128
+ evt[:dx] = 0
129
+ evt[:dy] = 0
130
+ evt[:mouse_data] = 0
131
+ evt[:flags] = User32::MOUSEEVENTF_LEFTDOWN
132
+ evt[:time] = 0
133
+ evt[:extra_info] = 0
134
+ User32.SendInput(1, [input], User32::Input.size)
135
+ end
136
+
137
+ def self.touch_release
138
+ input = User32::Input.new
139
+ input[:type] = User32::INPUT_MOUSE
140
+ evt = input[:evt][:mi]
141
+ evt[:dx] = 0
142
+ evt[:dy] = 0
143
+ evt[:mouse_data] = 0
144
+ evt[:flags] = User32::MOUSEEVENTF_LEFTUP
145
+ evt[:time] = 0
146
+ evt[:extra_info] = 0
147
+ User32.SendInput(1, [input], User32::Input.size)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,21 @@
1
+ module TwitchPlays
2
+ module OS
3
+ module X11
4
+ def self.press(key)
5
+ `xdotool key #{key}`
6
+ end
7
+
8
+ def self.touch_move(dx, dy)
9
+ `xdotool mousemove_relative --sync -- #{dx} #{dy}`
10
+ end
11
+
12
+ def self.touch_press
13
+ `xdotool mousedown 1`
14
+ end
15
+
16
+ def self.touch_release
17
+ `xdotool mouseup 1`
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ module TwitchPlays
2
+ case RbConfig::CONFIG['host_os']
3
+ when /win/
4
+ require_relative 'os/win'
5
+ Output = OS::Win
6
+ when /linux/
7
+ require_relative 'os/x11'
8
+ Output = OS::X11
9
+ end
10
+ end
@@ -1,100 +1,30 @@
1
+ require_relative 'os'
1
2
  require 'yaml'
2
- require 'ffi' if RbConfig::CONFIG['host_os'] =~ /win/
3
3
  require 'cinch'
4
4
 
5
5
  module TwitchPlays
6
- if RbConfig::CONFIG['host_os'] =~ /win/
7
- module Win
8
- extend FFI::Library
9
-
10
- INPUT_KEYBOARD = 1
11
-
12
- VK_BACK = 0x08
13
- VK_TAB = 0x09
14
- VK_RETURN = 0x0d
15
- VK_SHIFT = 0x10
16
- VK_CONTROL = 0x11
17
- VK_SPACE = 0x20
18
- VK_LEFT = 0x25
19
- VK_UP = 0x26
20
- VK_RIGHT = 0x27
21
- VK_DOWN = 0x28
22
- VK_F1 = 0x70
23
- VK_F2 = 0x71
24
- VK_F3 = 0x72
25
- VK_F4 = 0x73
26
- VK_F5 = 0x74
27
- VK_F6 = 0x75
28
- VK_F7 = 0x76
29
- VK_F8 = 0x77
30
- VK_F9 = 0x78
31
- VK_F10 = 0x79
32
- VK_F11 = 0x7a
33
- VK_F12 = 0x7b
34
-
35
- class KeyboardInput < FFI::Struct
36
- layout(:vk, :ushort,
37
- :scan, :ushort,
38
- :flags, :uint,
39
- :time, :uint,
40
- :extra_info, :pointer)
41
- end
42
-
43
- class InputEvent < FFI::Union
44
- layout :ki, KeyboardInput
45
-
46
- class Input < FFI::Struct
47
- layout(:type, :uint,
48
- :evt, InputEvent)
49
- end
50
-
51
- ffi_lib 'user32'
52
- ffi_convention :stdcall
53
-
54
- attach_function :SendInput, [:uint, :pointer, :int], :uint
55
- end
56
- end
57
-
58
- TRANSLATE_KEYS = {
59
- 'BackSpace' => Win::VK_BACK,
60
- 'Tab' => Win::VK_TAB,
61
- 'Return' => Win::VK_RETURN,
62
- 'Shift_L' => Win::VK_SHIFT,
63
- 'Shift_R' => Win::VK_SHIFT,
64
- 'Control_L' => Win::VK_CONTROL,
65
- 'Control_R' => Win::VK_CONTROL,
66
- 'space' => Win::VK_SPACE,
67
- 'Left' => Win::VK_LEFT,
68
- 'Up' => Win::VK_UP,
69
- 'Right' => Win::VK_RIGHT,
70
- 'Down' => Win::VK_DOWN,
71
- 'F1' => Win::VK_F1,
72
- 'F2' => Win::VK_F2,
73
- 'F3' => Win::VK_F3,
74
- 'F4' => Win::VK_F4,
75
- 'F5' => Win::VK_F5,
76
- 'F6' => Win::VK_F6,
77
- 'F7' => Win::VK_F7,
78
- 'F8' => Win::VK_F8,
79
- 'F9' => Win::VK_F9,
80
- 'F10' => Win::VK_F10,
81
- 'F11' => Win::VK_F11,
82
- 'F12' => Win::VK_F12
83
- }
84
- end
85
-
86
6
  class Plugin
87
7
  include Cinch::Plugin
88
8
 
9
+ plugin_name = 'TwitchPlaysPlugin'
89
10
  listen_to :connect, method: :on_connect
90
11
  listen_to :message, method: :on_message
91
12
 
92
13
  def initialize(*args)
93
14
  super
94
15
  @keys = config[:keys]
16
+ @use_touch = config[:use_touch]
17
+ touch_move_amount = config[:touch_move_amount]
18
+ @touch_directions = {
19
+ touch_up: [0, -touch_move_amount],
20
+ touch_down: [0, touch_move_amount],
21
+ touch_left: [-touch_move_amount, 0],
22
+ touch_right: [touch_move_amount, 0]
23
+ }
95
24
  @democracy_votes = 0
96
25
  @anarchy_votes = 0
97
- @democracy_mode = config[:start_in_democracy]
26
+ @use_democracy = config[:use_democracy]
27
+ @democracy_mode = @use_democracy && config[:start_in_democracy]
98
28
  @democracy_ratio_needed = config[:democracy_ratio_needed]
99
29
  @anarchy_ratio_needed = config[:anarchy_ratio_needed]
100
30
  @button_votes = Hash.new(0)
@@ -113,66 +43,60 @@ module TwitchPlays
113
43
  end
114
44
 
115
45
  def start_democracy
46
+ return if @democracy_mode
116
47
  @democracy_mode = true
117
48
  puts '---STARTING DEMOCRACY MODE---'
118
49
  @voting_thread = Thread.new do
119
50
  while @democracy_mode
120
- @button_votes.clear
121
- @total_votes = 0
51
+ synchronize(:votes_mutex) do
52
+ @button_votes.clear
53
+ @total_votes = 0
54
+ end
122
55
  sleep @voting_time
123
- next if @total_votes == 0
124
- out = 'VOTES:'
125
- percentages = @button_votes.sort {|(_, count1), (_, count2)|
126
- count1 <=> count2
127
- }.first(5).map {|(btn, count)|
128
- [btn, count.to_f / @total_votes * 100]
129
- }.each {|(btn, percent)|
130
- out << format_log_line(btn, "#{percent}%")
131
- }
132
- puts out
133
- winner = @button_votes.max_by {|(_, count)| count}
134
- next if winner.nil?
135
- press(@keys[winner[0]])
56
+ next if @total_votes.zero?
57
+ winner = synchronize(:votes_mutex) do
58
+ puts "VOTES:\n" + @button_votes.sort {|(_, count1), (_, count2)|
59
+ count1 <=> count2
60
+ }.first(5).map {|(btn, count)|
61
+ [btn, count.to_f / @total_votes * 100]
62
+ }.map {|(btn, percent)|
63
+ format_log_line(btn, "#{percent}%")
64
+ }.join("\n")
65
+ @button_votes.max_by {|(_, count)| count}[0]
66
+ end
67
+ case winner
68
+ when :touch_up, :touch_down, :touch_left, :touch_right
69
+ Output.touch_move(*@touch_directions[winner])
70
+ when :touch_press
71
+ Output.touch_press
72
+ when :touch_release
73
+ Output.touch_release
74
+ else
75
+ Output.press(@keys[winner])
76
+ end
136
77
  end
137
78
  end
138
79
  end
139
80
 
140
81
  def start_anarchy
82
+ return unless @democracy_mode
141
83
  @democracy_mode = false
142
84
  @voting_thread.join
85
+ @voting_thread = nil
143
86
  puts '---STARTING ANARCHY MODE---'
144
87
  end
145
88
 
146
- case RbConfig::CONFIG['host_os']
147
- when /linux/
148
- def press(key)
149
- `xdotool key #{key}`
150
- end
151
- when /win/
152
- def press(key)
153
- input = Win::Input.new
154
- input[:type] = Win::INPUT_KEYBOARD
155
- evt = input[:evt][:ki]
156
- evt.vk = TRANSLATE_KEYS[key] || key.upcase.ord
157
- evt.scan = 0
158
- evt.flags = 0
159
- evt.time = 0
160
- evt.extra_info = 0
161
- Win.SendInput(1, [input], Win::Input.size)
162
- end
163
- end
164
-
165
89
  def format_log_line(name, command)
166
90
  "#{name}#{' ' * (@log_line_length - name.length - command.length)}#{command}"
167
91
  end
168
92
 
169
93
  def on_connect(msg)
170
- if @use_savestates
171
- @savestate_thread = Thread.new do
172
- loop do
173
- sleep @savestate_interval
174
- press(@keys[:save_state])
175
- end
94
+ return unless @use_savestates
95
+ @savestate_thread = Thread.new do
96
+ key = @keys[:save_state]
97
+ loop do
98
+ sleep @savestate_interval
99
+ Output.press(key)
176
100
  end
177
101
  end
178
102
  end
@@ -181,26 +105,69 @@ module TwitchPlays
181
105
  message = msg.message
182
106
  case message
183
107
  when /^anarchy$/i
108
+ return unless @use_democracy
184
109
  puts format_log_line(msg.user.nick, message.downcase)
185
- @anarchy_votes += 1
186
- if @democracy_mode && calculate_ratio <= @anarchy_ratio_needed
187
- start_anarchy
110
+ synchronize(:votes_mutex) do
111
+ @anarchy_votes += 1
112
+ if @democracy_mode && calculate_ratio <= @anarchy_ratio_needed
113
+ start_anarchy
114
+ end
188
115
  end
189
116
  when /^democracy$/i
117
+ return unless @use_democracy
190
118
  puts format_log_line(msg.user.nick, message.downcase)
191
- @democracy_votes += 1
192
- if !@democracy_mode && calculate_ratio >= @democracy_ratio_needed
193
- start_democracy
119
+ synchronize(:votes_mutex) do
120
+ @democracy_votes += 1
121
+ if !@democracy_mode && calculate_ratio >= @democracy_ratio_needed
122
+ start_democracy
123
+ end
194
124
  end
195
- when /^up|down|left|right|a|b|x|y|l|r|start|select$/i
125
+ when /^(?:up|down|left|right|a|b|x|y|l|r|start|select)$/i
196
126
  btn = $&.to_sym
127
+ return unless @keys.has_key?(btn)
128
+ if @democracy_mode
129
+ synchronize(:votes_mutex) do
130
+ @total_votes += 1
131
+ @button_votes[btn] += 1
132
+ end
133
+ return
134
+ end
135
+ puts format_log_line(msg.user.nick, message.downcase)
136
+ Output.press(@keys[btn])
137
+ when /^(?:touch_up|touch_down|touch_left|touch_right)$/i
138
+ return unless @use_touch
139
+ btn = $&.to_sym
140
+ if @democracy_mode
141
+ synchronize(:votes_mutex) do
142
+ @total_votes += 1
143
+ @button_votes[btn] += 1
144
+ end
145
+ return
146
+ end
147
+ puts format_log_line(msg.user.nick, message.downcase)
148
+ Output.touch_move(*@touch_directions[btn])
149
+ when /^touch_press$/i
150
+ return unless @use_touch
197
151
  if @democracy_mode
198
- @total_votes += 1
199
- @button_votes[btn] += 1
152
+ synchronize(:votes_mutex) do
153
+ @total_votes += 1
154
+ @button_votes[$&.to_sym] += 1
155
+ end
200
156
  return
201
157
  end
202
- puts format_log_line(msg.user.nick, $&.downcase)
203
- press(@keys[btn])
158
+ puts format_log_line(msg.user.nick, message.downcase)
159
+ Output.touch_press
160
+ when /^touch_release$/i
161
+ return unless @use_touch
162
+ if @democracy_mode
163
+ synchronize(:votes_mutex) do
164
+ @total_votes += 1
165
+ @button_votes[$&.to_sym] += 1
166
+ end
167
+ return
168
+ end
169
+ puts format_log_line(msg.user.nick, message.downcase)
170
+ Output.touch_release
204
171
  end
205
172
  end
206
173
  end
@@ -218,6 +185,9 @@ module TwitchPlays
218
185
  c.plugins.options[Plugin] = config
219
186
  end
220
187
  end
188
+ trap('INT') do
189
+ bot.quit
190
+ end
221
191
  bot.start
222
192
  end
223
193
  end
@@ -1,3 +1,3 @@
1
1
  module TwitchPlays
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twitch_plays
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Steward
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-14 00:00:00.000000000 Z
11
+ date: 2014-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cinch
@@ -51,6 +51,9 @@ files:
51
51
  - bin/twitch_plays~
52
52
  - config.yml
53
53
  - lib/twitch_plays.rb
54
+ - lib/twitch_plays/os.rb
55
+ - lib/twitch_plays/os/win.rb
56
+ - lib/twitch_plays/os/x11.rb
54
57
  - lib/twitch_plays/twitch_plays.rb
55
58
  - lib/twitch_plays/version.rb
56
59
  homepage: https://github.com/filialpails/twitch_plays
@@ -73,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
76
  version: '0'
74
77
  requirements: []
75
78
  rubyforge_project:
76
- rubygems_version: 2.2.2
79
+ rubygems_version: 2.3.0
77
80
  signing_key:
78
81
  specification_version: 4
79
82
  summary: Yet another Twitch Plays Pokemon clone.