1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 | #!/usr/bin/env ruby
require 'curses'
require 'io/console'
require 'socket'
# $ gem install libnotify
require 'libnotify'
module ColorPair
DEFAULT = 0
TITLE = 1
end
def io_readable?(socket)
read_array = IO::select [socket], nil, nil, 0.0
if read_array && read_array[0]
read_array[0].include? socket
else
false
end
end
def info_from_str(str)
arr = str.split(',')
{
:ukn1 => arr[0].to_i,
:ukn2 => arr[1].to_i,
:ukn3 => arr[2].to_i,
:user_id => arr[3].to_i,
:user_name => arr[4],
:battle_id => arr[5].to_i,
:version => arr[6].to_i,
:hostname => arr[7],
:in_battle? => arr[8] == "0",
:ukn4 => arr[9].to_i,
:last_active => Time.now
}
end
def draw_title_color
Curses::setpos(0, 0)
Curses::attron(Curses::color_pair(ColorPair::TITLE)) do
Curses::addstr(" " * Curses::cols)
end
end
def draw_title(title)
Curses::setpos(0, 0)
Curses::attron(Curses::color_pair(ColorPair::TITLE)|Curses::A_BOLD) do
Curses::addstr(" " + title)
end
end
def draw_player(player, addr)
player_str = "- #{player[:last_active].strftime("%Y-%m-%d %H:%M:%S").ljust(19)} #{addr.ljust(15)} #{player[:in_battle?] ? "In battle" : "Idling "} #{player[:hostname]} #{player[:user_name]}\n"
Curses::addstr(player_str)
end
players = {}
if __FILE__ == $0
begin
Curses::init_screen
Curses::start_color
Curses::init_pair(ColorPair::TITLE, Curses::COLOR_WHITE, Curses::COLOR_MAGENTA)
Curses::use_default_colors
Curses::clear
draw_title_color
Curses::refresh
prog_start = Time.now
socket = UDPSocket.new
socket.bind "0.0.0.0", 1228
loop do
while io_readable?(socket)
data, addr = socket.recvfrom 1024
addr = addr[3]
player_info = info_from_str(data)
if Time.now - prog_start > 40.0 && !players.key?(addr)
# We notify new players starting from 40s after the program
# start (so that we detect all players that were already
# playing before notifying)
Libnotify.show(body: "#{player_info[:user_name]} started playing", summary: "Hearthstone", timeout: 3.0)
end
players[addr] = player_info
end
Curses::clear
draw_title_color
draw_title "Hearthstone connected players"
Curses::setpos(1, 0)
players.each { |addr,p| Libnotify.show(body: "#{p[:user_name]} stopped playing", summary: "Hearthstone", timeout: 3.0) if Time.now - p[:last_active] > 60.0 }
players.delete_if{ |addr,p| Time.now - p[:last_active] > 60.0 }
players.sort {|p1,p2| p1[1][:user_name] <=> p2[1][:user_name]}.to_h.each do |addr,player_info|
draw_player(player_info, addr)
end
Curses::refresh
sleep 1
end
rescue Interrupt
ensure
Curses::close_screen
end
end
|