talker 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +97 -0
- data/Rakefile +8 -0
- data/bin/talker-cat +32 -0
- data/bin/talker-shell +71 -0
- data/lib/talker/cli.rb +16 -0
- data/lib/talker.rb +136 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/talker.example.yml +7 -0
- data/spec/talker.yml +3 -0
- data/spec/talker_spec.rb +68 -0
- data/talker.gemspec +17 -0
- metadata +89 -0
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Talker Ruby Client
|
2
|
+
A real-time Talker Ruby client.
|
3
|
+
|
4
|
+
# Usage
|
5
|
+
1) Get a Talker account at https://talkerapp.com/signup
|
6
|
+
|
7
|
+
2) Get your Talker Token on https://myaccount.talkerapp.com/settings
|
8
|
+
|
9
|
+
3) Find the Room ID you want to connect to. This is the last part of the URL:
|
10
|
+
|
11
|
+
https://myaccount.talkerapp.com/rooms/<room_id>
|
12
|
+
|
13
|
+
4) Serve hot and enjoy
|
14
|
+
|
15
|
+
Talker.connect(:room => ROOM_ID, :token => YOUR_TALKER_TOKEN) do |client|
|
16
|
+
client.on_connected do
|
17
|
+
client.send_message "hello!"
|
18
|
+
end
|
19
|
+
|
20
|
+
client.on_message do |user, message|
|
21
|
+
puts user["name"] + ": " + message
|
22
|
+
end
|
23
|
+
|
24
|
+
trap("INT") { client.close }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Callbacks
|
28
|
+
All arguments are optional.
|
29
|
+
|
30
|
+
## <code>on_connected</code>
|
31
|
+
Called when the user is authenticated and ready to receive events.
|
32
|
+
|
33
|
+
## <code>on_presence(users)</code>
|
34
|
+
Called after <code>on_connected</code> with the list of connected users.
|
35
|
+
With <code>users</code> being something like this:
|
36
|
+
|
37
|
+
[{"id"=>1, "name"=>"macournoyer", "email"=>"macournoyer@talkerapp.com"},
|
38
|
+
{"id"=>2, "name"=>"gary", "email"=>"gary@talkerapp.com"}]
|
39
|
+
|
40
|
+
## <code>on_message(user, message, event)</code>
|
41
|
+
Called when a new message is received.
|
42
|
+
<code>user</code> is the sender. <code>event</code> contains the full event sent by the server.
|
43
|
+
|
44
|
+
## <code>on_join(user)</code>
|
45
|
+
Called when a user joins the room.
|
46
|
+
|
47
|
+
## <code>on_idle(user)</code>
|
48
|
+
Called when a user becomes idle (closed connection without leaving).
|
49
|
+
|
50
|
+
## <code>on_back(user)</code>
|
51
|
+
Called when a user is back from idle.
|
52
|
+
|
53
|
+
## <code>on_leave(user)</code>
|
54
|
+
Called when a user leaves.
|
55
|
+
|
56
|
+
## <code>on_close</code>
|
57
|
+
Called when the connection is closed.
|
58
|
+
|
59
|
+
## <code>on_error(error_message)</code>
|
60
|
+
Called an error is received from the Talker server.
|
61
|
+
|
62
|
+
# Methods
|
63
|
+
Methods of an instance of Talker class.
|
64
|
+
|
65
|
+
## <code>users</code>
|
66
|
+
Array of users currently in the room. In the form:
|
67
|
+
|
68
|
+
[{"id"=>1, "name"=>"macournoyer", "email"=>"macournoyer@talkerapp.com"},
|
69
|
+
{"id"=>2, "name"=>"gary", "email"=>"gary@talkerapp.com"}]
|
70
|
+
|
71
|
+
## <code>leave</code>
|
72
|
+
Leave the room and close the connection.
|
73
|
+
|
74
|
+
## <code>close</code>
|
75
|
+
Close the connection without leaving the room.
|
76
|
+
|
77
|
+
## <code>send_message(message)</code>
|
78
|
+
Send a message.
|
79
|
+
|
80
|
+
## <code>send_private_message(user_name, message)</code>
|
81
|
+
Send a private message to <code>user_name</code>.
|
82
|
+
|
83
|
+
# Running the specs
|
84
|
+
Howdy brave lil' one! To run the specs you'll need courage, hard work and some luck:
|
85
|
+
|
86
|
+
1) Install from source my "special" fork of em-spec at http://github.com/macournoyer/em-spec.
|
87
|
+
|
88
|
+
2) Edit the file spec/talker.example.yml with your info and rename it to spec/talker.yml.
|
89
|
+
|
90
|
+
3) Run <code>rake</code> and everything should be green, birds should start signing and someone will make you a chocolate cake
|
91
|
+
|
92
|
+
(results may vary).
|
93
|
+
|
94
|
+
# Credits & License
|
95
|
+
Released under the Ruby License, (c) Talker
|
96
|
+
|
97
|
+
Thanks to http://github.com/raggi for kicking this off.
|
data/Rakefile
ADDED
data/bin/talker-cat
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "talker"
|
3
|
+
require "talker/cli"
|
4
|
+
|
5
|
+
token = Talker::CLI.load_token
|
6
|
+
room = ARGV.first
|
7
|
+
|
8
|
+
abort <<-EOS unless room
|
9
|
+
usage: echo 'something' | talker-cat <room_id>
|
10
|
+
|
11
|
+
Send a message to a room:
|
12
|
+
|
13
|
+
echo 'something' | talker-cat 1
|
14
|
+
|
15
|
+
Send a file to a room:
|
16
|
+
|
17
|
+
talker-cat 1 < site_fixer.rb
|
18
|
+
|
19
|
+
Room ID is the last part of the URL:
|
20
|
+
https://myaccount.talkerapp.com/rooms/<room_id>
|
21
|
+
|
22
|
+
EOS
|
23
|
+
|
24
|
+
Talker.connect(:room => room, :token => token) do |client|
|
25
|
+
client.on_connected do
|
26
|
+
client.send_message STDIN.read
|
27
|
+
client.close
|
28
|
+
end
|
29
|
+
client.on_error do |error|
|
30
|
+
puts error
|
31
|
+
end
|
32
|
+
end
|
data/bin/talker-shell
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "talker"
|
3
|
+
require "talker/cli"
|
4
|
+
|
5
|
+
token = Talker::CLI.load_token
|
6
|
+
room, cmd, process_name = *ARGV
|
7
|
+
process_name ||= cmd
|
8
|
+
|
9
|
+
abort <<-EOS unless room && cmd
|
10
|
+
usage: talker-shell <room_id> <command> [name]
|
11
|
+
Launch an irb session connected to a room:
|
12
|
+
|
13
|
+
talker-shell 1 irb
|
14
|
+
|
15
|
+
Room ID is the last part of the URL:
|
16
|
+
https://myaccount.talkerapp.com/rooms/<room_id>
|
17
|
+
|
18
|
+
EOS
|
19
|
+
|
20
|
+
|
21
|
+
module Process
|
22
|
+
attr_accessor :name, :client
|
23
|
+
|
24
|
+
def receive_data(data)
|
25
|
+
log data
|
26
|
+
end
|
27
|
+
|
28
|
+
def unbind
|
29
|
+
log "exited"
|
30
|
+
@client.close
|
31
|
+
end
|
32
|
+
|
33
|
+
def log(msg)
|
34
|
+
puts "> #{msg}"
|
35
|
+
@client.send_message("#{name}> #{msg}")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
EM.run do
|
41
|
+
Talker.connect(:room => room, :token => token) do |client|
|
42
|
+
@process = nil
|
43
|
+
|
44
|
+
client.on_connected do
|
45
|
+
EM.popen(cmd, Process) do |process|
|
46
|
+
client.send_message "Connected to process: #{cmd}, prefix commands w/ '#{process_name}: '"
|
47
|
+
|
48
|
+
process.name = process_name
|
49
|
+
process.client = client
|
50
|
+
trap("INT") { process.close_connection_after_writing }
|
51
|
+
|
52
|
+
@process = process
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
client.on_message do |user, message|
|
57
|
+
if command = message[/^#{process_name}: (.*)$/, 1]
|
58
|
+
puts "> #{cmd}"
|
59
|
+
@process.send_data command + "\n"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
client.on_error do |error|
|
64
|
+
puts error
|
65
|
+
end
|
66
|
+
|
67
|
+
client.on_close do
|
68
|
+
EM.stop
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/talker/cli.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Talker
|
2
|
+
class CLI
|
3
|
+
def self.load_token
|
4
|
+
token_path = File.join(ENV['HOME'], '.talker')
|
5
|
+
|
6
|
+
unless File.file?(token_path)
|
7
|
+
abort <<-EOS
|
8
|
+
Place your Talker Token in #{token_path}. You can find your
|
9
|
+
token in https://myaccount.talkerapp.com/settings
|
10
|
+
EOS
|
11
|
+
end
|
12
|
+
|
13
|
+
File.read(token_path).strip
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/talker.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
require "yajl"
|
3
|
+
|
4
|
+
class Talker < EM::Connection
|
5
|
+
class Error < RuntimeError; end
|
6
|
+
|
7
|
+
attr_accessor :room, :token, :thread
|
8
|
+
|
9
|
+
def self.connect(options={})
|
10
|
+
host = options[:host] || "talkerapp.com"
|
11
|
+
port = (options[:port] || 8500).to_i
|
12
|
+
room = options[:room].to_i
|
13
|
+
token = options[:token]
|
14
|
+
|
15
|
+
thread = Thread.new { EM.run } unless EM.reactor_running?
|
16
|
+
|
17
|
+
EM.connect host, port, self do |c|
|
18
|
+
c.thread = thread
|
19
|
+
c.room = room
|
20
|
+
c.token = token
|
21
|
+
yield c if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
thread.join unless thread.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@users = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
# Callbacks
|
32
|
+
%w( connected message join idle back leave presence error close ).each do |method|
|
33
|
+
class_eval <<-EOS
|
34
|
+
def on_#{method}(&block)
|
35
|
+
@on_#{method} = block
|
36
|
+
end
|
37
|
+
EOS
|
38
|
+
end
|
39
|
+
|
40
|
+
def users
|
41
|
+
@users.values
|
42
|
+
end
|
43
|
+
|
44
|
+
def send_message(message, attributes={})
|
45
|
+
send({ :type => "message", :content => message }.merge(attributes))
|
46
|
+
end
|
47
|
+
|
48
|
+
def send_private_message(to, message)
|
49
|
+
if to.is_a?(String)
|
50
|
+
user = @users.values.detect { |user| user["name"] == to }
|
51
|
+
raise Error, "User #{to} not found" unless user
|
52
|
+
user_id = user["id"]
|
53
|
+
else
|
54
|
+
user_id = to
|
55
|
+
end
|
56
|
+
send_message message, :to => user_id
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
## EventMachine callbacks
|
61
|
+
|
62
|
+
def connection_completed
|
63
|
+
send :type => "connect", :room => @room, :token => @token
|
64
|
+
EM.add_periodic_timer(20) { send :type => "ping" }
|
65
|
+
end
|
66
|
+
|
67
|
+
def leave
|
68
|
+
send :type => "close"
|
69
|
+
close
|
70
|
+
end
|
71
|
+
|
72
|
+
def close
|
73
|
+
close_connection_after_writing
|
74
|
+
end
|
75
|
+
|
76
|
+
def post_init
|
77
|
+
@parser = Yajl::Parser.new
|
78
|
+
@parser.on_parse_complete = method(:event_parsed)
|
79
|
+
end
|
80
|
+
|
81
|
+
def receive_data(data)
|
82
|
+
@parser << data
|
83
|
+
end
|
84
|
+
|
85
|
+
def unbind
|
86
|
+
trigger :close
|
87
|
+
@thread.kill if @thread
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
private
|
92
|
+
def event_parsed(event)
|
93
|
+
case event["type"]
|
94
|
+
when "connected"
|
95
|
+
trigger :connected
|
96
|
+
when "error"
|
97
|
+
if @on_error
|
98
|
+
@on_error.call(event["message"])
|
99
|
+
else
|
100
|
+
raise Error, event["message"]
|
101
|
+
end
|
102
|
+
when "users"
|
103
|
+
event["users"].each do |user|
|
104
|
+
@users[user["id"]] = user
|
105
|
+
end
|
106
|
+
trigger :presence, @users.values
|
107
|
+
when "join"
|
108
|
+
@users[event["user"]["id"]] = event["user"]
|
109
|
+
trigger :join, event["user"]
|
110
|
+
when "leave"
|
111
|
+
@users.delete(event["user"]["id"])
|
112
|
+
trigger :leave, event["user"]
|
113
|
+
when "idle"
|
114
|
+
trigger :idle, event["user"]
|
115
|
+
when "back"
|
116
|
+
trigger :back, event["user"]
|
117
|
+
when "message"
|
118
|
+
@users[event["user"]["id"]] ||= event["user"]
|
119
|
+
trigger :message, event["user"], event["content"], event
|
120
|
+
else
|
121
|
+
raise Error, "unknown event type received from server: " + event["type"]
|
122
|
+
end
|
123
|
+
rescue
|
124
|
+
close
|
125
|
+
raise
|
126
|
+
end
|
127
|
+
|
128
|
+
def trigger(callback, *args)
|
129
|
+
callback = instance_variable_get(:"@on_#{callback}")
|
130
|
+
callback.call(*args[0,callback.arity]) if callback
|
131
|
+
end
|
132
|
+
|
133
|
+
def send(data)
|
134
|
+
send_data Yajl::Encoder.encode(data) + "\n"
|
135
|
+
end
|
136
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "yaml"
|
3
|
+
require 'spec'
|
4
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
5
|
+
require "talker"
|
6
|
+
|
7
|
+
# Installing em-spec from http://github.com/macournoyer/em-spec
|
8
|
+
require 'em/spec'
|
9
|
+
require 'em/spec/rspec'
|
10
|
+
EM.spec_backend = EM::Spec::Rspec
|
11
|
+
|
12
|
+
TALKER_CONFIG = YAML.load_file(File.dirname(__FILE__) + "/talker.yml")
|
13
|
+
|
14
|
+
module Helpers
|
15
|
+
def connect(&callback)
|
16
|
+
Talker.connect :room => TALKER_CONFIG["room"].to_i, :token => TALKER_CONFIG["token"], &callback
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Spec::Runner.configure do |config|
|
21
|
+
config.include Helpers
|
22
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Get your Talker Token on https://myaccount.talkerapp.com/settings
|
2
|
+
token: YOUR_TALKER_TOKEN
|
3
|
+
# Room ID is the last part of the URL:
|
4
|
+
# https://myaccount.talkerapp.com/rooms/<room_id>
|
5
|
+
room: ROOM_ID
|
6
|
+
# Make sure it matches or some specs will fail
|
7
|
+
user_name: YOUR_USER_NAME
|
data/spec/talker.yml
ADDED
data/spec/talker_spec.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
EM.describe Talker do
|
4
|
+
it "should connect" do
|
5
|
+
connect do |client|
|
6
|
+
client.on_connected do
|
7
|
+
done
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should close" do
|
13
|
+
connect do |client|
|
14
|
+
client.on_connected do
|
15
|
+
client.close
|
16
|
+
end
|
17
|
+
client.on_close do
|
18
|
+
done
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should receive presence" do
|
24
|
+
connect do |client|
|
25
|
+
client.on_presence do |users|
|
26
|
+
users.size.should >= 1
|
27
|
+
users.map { |user| user["name"] }.should include(TALKER_CONFIG["user_name"])
|
28
|
+
done
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should send and receive message" do
|
34
|
+
connect do |client|
|
35
|
+
client.on_connected do
|
36
|
+
client.send_message "it works, magic!"
|
37
|
+
end
|
38
|
+
client.on_message do |user, message|
|
39
|
+
message.should == "it works, magic!"
|
40
|
+
done
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should send and receive private message" do
|
46
|
+
connect do |client|
|
47
|
+
client.on_presence do
|
48
|
+
client.send_private_message TALKER_CONFIG["user_name"], "private magic"
|
49
|
+
end
|
50
|
+
client.on_message do |user, message|
|
51
|
+
message.should == "private magic"
|
52
|
+
done
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Keep at the end, mmmkay?
|
58
|
+
it "should leave" do
|
59
|
+
connect do |client|
|
60
|
+
client.on_connected do
|
61
|
+
client.leave
|
62
|
+
end
|
63
|
+
client.on_close do
|
64
|
+
done
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/talker.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{talker}
|
3
|
+
s.version = "0.0.1"
|
4
|
+
|
5
|
+
s.authors = ["Marc-Andre Cournoyer"]
|
6
|
+
s.email = "macournoyer@talkerapp.com"
|
7
|
+
s.files = Dir["**/*"]
|
8
|
+
s.homepage = "http://github.com/macournoyer/talker.rb"
|
9
|
+
s.require_paths = ["lib"]
|
10
|
+
s.bindir = "bin"
|
11
|
+
s.executables = ["talker-cat", "talker-shell"]
|
12
|
+
s.summary = "A real-time Talker Ruby client."
|
13
|
+
s.test_files = Dir["spec/**"]
|
14
|
+
|
15
|
+
s.add_dependency "eventmachine"
|
16
|
+
s.add_dependency "yajl-ruby"
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: talker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marc-Andre Cournoyer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-12 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: eventmachine
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: yajl-ruby
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email: macournoyer@talkerapp.com
|
37
|
+
executables:
|
38
|
+
- talker-cat
|
39
|
+
- talker-shell
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
files:
|
45
|
+
- bin/talker-cat
|
46
|
+
- bin/talker-shell
|
47
|
+
- lib/talker/cli.rb
|
48
|
+
- lib/talker.rb
|
49
|
+
- Rakefile
|
50
|
+
- README.md
|
51
|
+
- spec/spec_helper.rb
|
52
|
+
- spec/talker.example.yml
|
53
|
+
- spec/talker.yml
|
54
|
+
- spec/talker_spec.rb
|
55
|
+
- talker-0.0.1.gem
|
56
|
+
- talker.gemspec
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/macournoyer/talker.rb
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
version:
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.3.5
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: A real-time Talker Ruby client.
|
85
|
+
test_files:
|
86
|
+
- spec/spec_helper.rb
|
87
|
+
- spec/talker.example.yml
|
88
|
+
- spec/talker.yml
|
89
|
+
- spec/talker_spec.rb
|