zulip 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +79 -0
  6. data/Rakefile +1 -0
  7. data/example/zulip_message_echo.rb +26 -0
  8. data/lib/zulip/client/event_parser.rb +14 -0
  9. data/lib/zulip/client/event_streaming.rb +86 -0
  10. data/lib/zulip/client/messages.rb +31 -0
  11. data/lib/zulip/client/queue_registration.rb +25 -0
  12. data/lib/zulip/client/stream_subscriptions.rb +40 -0
  13. data/lib/zulip/client/users.rb +16 -0
  14. data/lib/zulip/client.rb +53 -0
  15. data/lib/zulip/message.rb +18 -0
  16. data/lib/zulip/stream_subscription.rb +12 -0
  17. data/lib/zulip/user.rb +9 -0
  18. data/lib/zulip/version.rb +3 -0
  19. data/lib/zulip.rb +7 -0
  20. data/spec/fixtures/add-subscription.json +1 -0
  21. data/spec/fixtures/get-message-event-success.json +1 -0
  22. data/spec/fixtures/get-private-message.json +1 -0
  23. data/spec/fixtures/get-user-subscriptions.json +1 -0
  24. data/spec/fixtures/get-users.json +1 -0
  25. data/spec/fixtures/post-message-to-stream.json +1 -0
  26. data/spec/fixtures/queue-registration-success.json +1 -0
  27. data/spec/fixtures/sending-private-message-success.json +1 -0
  28. data/spec/helper.rb +13 -0
  29. data/spec/lib/zulip/client/event_parser_spec.rb +19 -0
  30. data/spec/lib/zulip/client/event_streaming_spec.rb +85 -0
  31. data/spec/lib/zulip/client/messages_spec.rb +64 -0
  32. data/spec/lib/zulip/client/queue_registration_spec.rb +44 -0
  33. data/spec/lib/zulip/client/stream_subscriptions_spec.rb +42 -0
  34. data/spec/lib/zulip/client/users_spec.rb +24 -0
  35. data/spec/lib/zulip/client_spec.rb +16 -0
  36. data/spec/lib/zulip/message_spec.rb +79 -0
  37. data/spec/lib/zulip/stream_subscription_spec.rb +21 -0
  38. data/spec/lib/zulip/user_spec.rb +17 -0
  39. data/zulip-rb.gemspec +27 -0
  40. metadata +177 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7104e27dedb5006e5b02e35bf0a8cadf8c74985b
4
+ data.tar.gz: a33826034e2546c0ef6957b1e41bde933ae5b342
5
+ SHA512:
6
+ metadata.gz: 4597dad59bb5b8269d3149a91c4629a6cf1f455ea961632b6a351cdcb39a1486e5d6a264609bcce36942b9910aea1b357729e5da9116a0c4e5ddca24929fdd40
7
+ data.tar.gz: ae289b999a7ebdd71d3504263de80d1255aba4f38aa4c3c141fa80ee28470774793a91e55bbb0bad6c961741cbe321f55725c2140c6302c1d823b20a247d0b5a
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ryan Vergeront
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # Zulip-Rb
2
+
3
+ A ruby interface to the Zulip API.
4
+
5
+ ### Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ `gem 'zulip-rb'`
10
+
11
+ And then execute:
12
+
13
+ `$ bundle`
14
+
15
+ Or install it yourself as:
16
+
17
+ `$ gem install zulip-rb`
18
+
19
+ ### Configuration
20
+ You'll need to register a bot to use the Zulip API.
21
+ You can obtain your Zulip API key, create bots, and manage bots all
22
+ from your Zulip [settings page](https://zulip.com/#settings).
23
+
24
+ Set your api key and your bot's email address by passing a block to your Zulip::Client instance:
25
+ ```ruby
26
+ client = Zulip::Client.new do |config|
27
+ config.bot_email_address = "YOUR_BOTS_EMAIL_ADDRESS"
28
+ config.api_key = "YOUR_API_KEY"
29
+ end
30
+ ```
31
+
32
+ You'll need to subscribe your bot to streams you want to read from. Do so by adding your bot's email to the stream's membership from a zulip client, or use the #subscribe method (see example below).
33
+
34
+ ### Usage
35
+
36
+ Send messages to a stream:
37
+ ```ruby
38
+ client.send_message("Hey", "I'm posting to zulip", "test-stream")
39
+ ```
40
+
41
+ Send private messages to one or more users:
42
+ ```ruby
43
+ client.send_private_message("hey I heard you like the internet", "bob@the-internet.net", "alice@the-information-superhighway.org")
44
+ ```
45
+
46
+ Stream messages:
47
+ ```ruby
48
+ client.stream_messages do |message|
49
+ # Do some work
50
+ end
51
+ ```
52
+
53
+ Get your bot's current subscriptions:
54
+ ```ruby
55
+ client.get_subscriptions
56
+ ```
57
+
58
+ Subscribe your bot to a stream, passing the stream name:
59
+ ```ruby
60
+ client.subscribe "food"
61
+ ```
62
+
63
+ List users:
64
+ ```ruby
65
+ client.get_users
66
+ ```
67
+
68
+ ### Contributing
69
+
70
+ 1. Fork it
71
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
72
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
73
+ 4. Push to the branch (`git push origin my-new-feature`)
74
+ 5. Create new Pull Request
75
+
76
+ Some areas that could use contributions:
77
+
78
+ 1. Error handling
79
+ 2. Add unsubscribing from a stream
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ $:.push File.expand_path('../../lib', __FILE__)
2
+ require 'zulip'
3
+
4
+ ### Sends a private message to a new thread is posted to a specified stream
5
+
6
+ class ZulipEcho
7
+
8
+ def run
9
+ client = Zulip::Client.new do |config|
10
+ config.bot_email_address = ENV['BOT_EMAIL_ADDRESS'] || "bot@example.com"
11
+ config.api_key = ENV['BOT_API_KEY'] || "apikey"
12
+ end
13
+
14
+ client.stream_messages do |message|
15
+ client.send_private_message(private_message_content(message), "example@gmail.com")
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def private_message_content(message)
22
+ "#{message.sender_full_name}, posted to #{message.stream}:\n #{message.subject}: #{message.content}"
23
+ end
24
+ end
25
+
26
+ ZulipEcho.new.run
@@ -0,0 +1,14 @@
1
+ module Zulip
2
+ class Client
3
+ module EventParser
4
+ def self.parse(event)
5
+ case event['type']
6
+ when "message"
7
+ Zulip::Message.new event.fetch('message')
8
+ when "heartbeat"
9
+ # NOOP
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,86 @@
1
+ module Zulip
2
+ class Client
3
+ module EventStreaming
4
+
5
+ def stream_messages(&block)
6
+ stream_events("message", &block)
7
+ end
8
+
9
+ def stream_private_messages(&block)
10
+ stream_raw_events("message") do |raw_event|
11
+ yield parse_event(raw_event) if private_message?(raw_event)
12
+ end
13
+ end
14
+
15
+ def stream_public_messages(&block)
16
+ stream_raw_events("message") do |raw_event|
17
+ yield parse_event(raw_event) if public_message?(raw_event)
18
+ end
19
+ end
20
+
21
+ def stream_events(event_types, &block)
22
+ stream_raw_events(event_types) do |raw_event|
23
+ yield parse_event(raw_event)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def stream_raw_events(event_types, &block)
30
+ queue = register(event_types)
31
+ queue_id = queue.queue_id
32
+ last_event_id = queue.last_event_id
33
+
34
+ loop do
35
+ events = get_events(queue_id, last_event_id)
36
+ last_event_id = max_event_id_from(events)
37
+
38
+ events.each do |event|
39
+ yield event if event_types.include?(event["type"])
40
+ end
41
+ end
42
+ end
43
+
44
+ # Makes a longpulling request on an event queue
45
+ def get_events(queue_id, last_event_id=nil)
46
+ connection.params = build_get_event_params(queue_id, last_event_id)
47
+ event_response.fetch("events")
48
+ end
49
+
50
+ def parse_event(raw_event)
51
+ Zulip::Client::EventParser.parse(raw_event)
52
+ end
53
+
54
+ def build_get_event_params(registered_queue_or_queue_id, last_event_id)
55
+ { 'queue_id' => find_queue_id(registered_queue_or_queue_id),
56
+ 'last_event_id' => last_event_id || registered_queue_or_queue_id.last_event_id }
57
+ end
58
+
59
+ def find_queue_id(registered_queue_or_queue_id)
60
+ if registered_queue_or_queue_id.respond_to?(:queue_id)
61
+ registered_queue_or_queue_id.queue_id
62
+ else
63
+ registered_queue_or_queue_id
64
+ end
65
+ end
66
+
67
+ def event_response
68
+ response = connection.get("/v1/events").body
69
+ JSON.parse(response)
70
+ end
71
+
72
+ def max_event_id_from(events)
73
+ events.map{ |event| event['id']}.max
74
+ end
75
+
76
+ def private_message?(event)
77
+ event['message']['type'] == "private"
78
+ end
79
+
80
+ def public_message?(event)
81
+ event['message']['type'] == "stream"
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,31 @@
1
+ require 'faraday'
2
+
3
+ module Zulip
4
+ class Client
5
+ module Messages
6
+
7
+ def send_message(subject, content, to_stream)
8
+ post_message("stream", content, to_stream, subject)
9
+ end
10
+
11
+ def send_private_message(content, *recipient_users)
12
+ post_message("private", content, recipient_users)
13
+ end
14
+
15
+ private
16
+
17
+ def post_message(type, content, recipients_or_stream, subject=nil)
18
+ connection.params = build_post_message_params(type, content,
19
+ recipients_or_stream, subject)
20
+ connection.post("v1/messages")
21
+ end
22
+
23
+ def build_post_message_params(type, content, recipients_or_stream, subject=nil)
24
+ params = subject ? {"subject" => subject } : {}
25
+ params.merge({ "type" => type, "content" => content,
26
+ "to" => json_encode_list(recipients_or_stream) })
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ module Zulip
2
+ class Client
3
+ module QueueRegistration
4
+ EVENT_TYPES = { message: "message" }
5
+ # TODO: Add support for other event types
6
+ # TODO: Add support setting apply markdown to false
7
+
8
+ QueueRegistrationResponse = Struct.new(:queue_id, :last_event_id)
9
+
10
+ def register(event_types=nil, opts={})
11
+
12
+ if event_types
13
+ connection.params = { "event_types" => json_encode_list(event_types) }
14
+ end
15
+
16
+ QueueRegistrationResponse.new( registration_response['queue_id'],
17
+ registration_response['last_event_id'] )
18
+ end
19
+
20
+ def registration_response
21
+ @registration_response ||= parse_json(connection.post("v1/register").body)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ module Zulip
2
+ class Client
3
+ module StreamSubscriptions
4
+
5
+ def get_subscriptions
6
+ get_subscriptions_response.map do |subscription|
7
+ Zulip::StreamSubscription.new(subscription)
8
+ end
9
+ end
10
+
11
+ def subscribe(streams)
12
+ patch_subscriptions subscribe_params(streams)
13
+ end
14
+
15
+ private
16
+
17
+ def get_subscriptions_response
18
+ parse_response(connection.get(subscription_urn)).fetch("subscriptions") { [] }
19
+ end
20
+
21
+ def subscription_urn
22
+ 'v1/users/me/subscriptions'
23
+ end
24
+
25
+ def subscribe_params(streams)
26
+ { add: json_encoded_streams(streams) }
27
+ end
28
+
29
+ def json_encoded_streams(streams)
30
+ streams = Array(streams).map { |subscription_name| { name: subscription_name } }
31
+ json_encode_list(streams)
32
+ end
33
+
34
+ def patch_subscriptions(req_params)
35
+ parse_response(connection.patch(subscription_urn, req_params))
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,16 @@
1
+ module Zulip
2
+ class Client
3
+ module Users
4
+ def get_users
5
+ get_users_response.map { |user_data| Zulip::User.new(user_data) }
6
+ end
7
+
8
+ private
9
+
10
+ def get_users_response
11
+ parse_response(connection.get('v1/users')).fetch("members") { [] }
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,53 @@
1
+ require 'json'
2
+ require 'faraday'
3
+
4
+ require 'zulip/client/messages'
5
+ require 'zulip/client/users'
6
+ require 'zulip/client/stream_subscriptions'
7
+ require 'zulip/client/queue_registration'
8
+ require 'zulip/client/event_streaming'
9
+ require 'zulip/client/event_parser'
10
+
11
+ module Zulip
12
+ class Client
13
+ include Zulip::Client::Messages
14
+ include Zulip::Client::Users
15
+ include Zulip::Client::StreamSubscriptions
16
+ include Zulip::Client::QueueRegistration
17
+ include Zulip::Client::EventStreaming
18
+ include Zulip::Client::EventParser
19
+
20
+ attr_accessor :bot_email_address, :api_key
21
+ attr_writer :connection
22
+ ENDPOINT = "https://api.zulip.com"
23
+
24
+ def initialize
25
+ yield self if block_given?
26
+ end
27
+
28
+ def connection
29
+ @connection ||= initialize_connection
30
+ end
31
+
32
+ private
33
+
34
+ def initialize_connection
35
+ conn = Faraday.new(url: ENDPOINT)
36
+ conn.basic_auth(bot_email_address, api_key)
37
+ conn
38
+ end
39
+
40
+ def json_encode_list(items)
41
+ JSON.generate(Array(items).flatten)
42
+ end
43
+
44
+ def parse_response(http_response)
45
+ parse_json(http_response.body)
46
+ end
47
+
48
+ def parse_json(json)
49
+ JSON.parse(json)
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,18 @@
1
+ module Zulip
2
+ class Message
3
+ attr_reader :id, :recipient_id, :sender_email, :timestamp, :display_recipient,
4
+ :sender_id, :sender_full_name, :sender_domain, :content, :gravatar_hash,
5
+ :avatar_url, :client, :content_type, :subject_links, :sender_short_name,
6
+ :type, :subject
7
+
8
+ alias :stream :display_recipient
9
+ alias :display_recipients :display_recipient
10
+
11
+ def initialize(attrs={})
12
+ attrs.each do |name, value|
13
+ instance_variable_set("@#{name}", value)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module Zulip
2
+ class StreamSubscription
3
+ attr_reader :name, :color, :notifications, :subscribers, :invite_only,
4
+ :email_address, :in_home_view
5
+
6
+ def initialize(attrs={})
7
+ attrs.each do |name, value|
8
+ instance_variable_set("@#{name}", value)
9
+ end
10
+ end
11
+ end
12
+ end
data/lib/zulip/user.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Zulip
2
+ class User
3
+ attr_reader :email, :full_name
4
+ def initialize(user_data)
5
+ @email = user_data['email']
6
+ @full_name = user_data['full_name']
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Zulip
2
+ VERSION = "0.0.1"
3
+ end
data/lib/zulip.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+
3
+ require 'zulip/version'
4
+ require 'zulip/client'
5
+ require 'zulip/message'
6
+ require 'zulip/user'
7
+ require 'zulip/stream_subscription'
@@ -0,0 +1 @@
1
+ {"subscribed":{"test_zulip_cli-bot@students.hackerschool.com":["clojure"]},"not_subscribed":[],"result":"success","msg":"","already_subscribed":{"test_zulip_cli-bot@students.hackerschool.com":["ruby"]},"removed":[]}
@@ -0,0 +1 @@
1
+ {"result":"success","queue_id":"1384457534:662","msg":"","events":[{"flags":[],"message":{"recipient_id":23442,"sender_email":"ryanvergeront@gmail.com","timestamp":1384460690,"display_recipient":"test-stream","sender_id":2874,"sender_full_name":"Ryan Vergeront (F'13)","sender_domain":"students.hackerschool.com","content":"register an event type of 'message' not 'messages'","gravatar_hash":"a8e2e659c929c79669f97603bd73fcc5","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/a8e2e659c929c79669f97603bd73fcc5?d=identicon","client":"desktop app Mac 0.3.10","content_type":"text\/x-markdown","subject_links":[],"sender_short_name":"ryanvergeront","type":"stream","id":12791579,"subject":"sending some internet websites to the internet"},"type":"message","id":0}]}
@@ -0,0 +1 @@
1
+ {"result":"success","queue_id":"1384457534:662","msg":"","events":[{"flags":[],"message":{"recipient_id":25207,"sender_email":"ryanvergeront@gmail.com","timestamp":1384561238,"display_recipient":[{"full_name":"Ryan Vergeront (F'13)","domain":"students.hackerschool.com","email":"ryanvergeront@gmail.com","short_name":"ryanvergeront","id":2874},{"domain":"students.hackerschool.com","short_name":"test_zulip_cli-bot","email":"test_zulip_cli-bot@students.hackerschool.com","full_name":"Ryan's Bot","id":2934}],"sender_id":2874,"sender_full_name":"Ryan Vergeront (F'13)","sender_domain":"students.hackerschool.com","content":"the messages are less funny when I'm PMing myself","gravatar_hash":"a8e2e659c929c79669f97603bd73fcc5","avatar_url":"https://secure.gravatar.com/avatar/a8e2e659c929c79669f97603bd73fcc5?d=identicon","client":"desktop app Mac 0.3.10","content_type":"text/x-markdown","subject_links":[],"sender_short_name":"ryanvergeront","type":"private","id":12855410,"subject":""},"type":"message","id":0}]}
@@ -0,0 +1 @@
1
+ {"msg":"","result":"success","subscriptions":[{"name":"VIM","color":"#76ce90","notifications":false,"subscribers":["bob@example.com","alice@example.com"],"invite_only":false,"email_address":"VIM+c46cdd8670afe104a66e5f1af277aa05@streams.zulip.com","in_home_view":true},{"name":"social","color":"#94c849","notifications":false,"subscribers":["alice@example.com","jane@example.com"],"invite_only":false,"email_address":"social+06435131f9e681a9835c2733893baad2@streams.zulip.com","in_home_view":true}]}
@@ -0,0 +1 @@
1
+ {"msg":"","result":"success","members":[{"email":"bob@example.org","full_name":"Bob G"},{"email":"alice@example.org","full_name":"Alice P"}]}
@@ -0,0 +1 @@
1
+ {"msg":"","result":"success","id":12743014}
@@ -0,0 +1 @@
1
+ {"msg":"","max_message_id":12779330,"last_event_id":-1,"result":"success","queue_id":"1384438093:1161"}
@@ -0,0 +1 @@
1
+ {"msg":"","result":"success","id":12745890}
data/spec/helper.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+
3
+ def parsed_json_fixture(file)
4
+ JSON.parse(fixture(file))
5
+ end
6
+
7
+ def fixture(file)
8
+ File.new(fixture_path + '/' + file).read
9
+ end
10
+
11
+ def fixture_path
12
+ File.expand_path("../fixtures", __FILE__)
13
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "../../../../lib/zulip"
2
+ require 'helper'
3
+
4
+ module Zulip
5
+ class Client
6
+ describe EventParser do
7
+ it "parses message events" do
8
+ event_hash = parsed_json_fixture("get-message-event-success.json")['events'].first
9
+ result = Zulip::Client::EventParser.parse(event_hash)
10
+ expect(result).to be_kind_of Zulip::Message
11
+ end
12
+
13
+ it "parses heartbeat events"
14
+ it "parses presence events"
15
+ it "parses subscriptions events"
16
+ it "parses error events"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,85 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ class ExitStreamingGracefullyError < StandardError; end
5
+
6
+ module Zulip
7
+ class Client
8
+ describe EventStreaming do
9
+ let(:private_message_fixture) { fixture("get-private-message.json") }
10
+ let(:public_message_fixture) { fixture("get-message-event-success.json") }
11
+
12
+ context "streaming messages" do
13
+
14
+ let(:client) { Zulip::Client.new }
15
+ let(:fake_connection) { double("fake connection", :params= => nil) }
16
+ let(:fake_response) { double("response") }
17
+ let(:fake_queue) { double("fake queue", queue_id: "id", last_event_id: -1) }
18
+
19
+ describe "#stream_private_messages" do
20
+ it "returns private messages viewable to the bot" do
21
+
22
+ # Returns a public-message, then a private message
23
+ fake_response.stub(:body).and_return(public_message_fixture, private_message_fixture)
24
+ fake_connection.stub(:get).with('/v1/events').and_return(fake_response)
25
+ client.stub(:register).and_return(fake_queue)
26
+
27
+ client.connection = fake_connection
28
+
29
+ messages = []
30
+ begin
31
+ client.stream_private_messages do |msg|
32
+ messages << msg
33
+ raise ExitStreamingGracefullyError if messages.count == 2
34
+ end
35
+ rescue ExitStreamingGracefullyError
36
+ messages.each { |msg| expect(msg.type).to eq("private") }
37
+ end
38
+ end
39
+ end
40
+
41
+ describe "#stream_public_messages" do
42
+ it "only returns public messages" do
43
+ fake_response.stub(:body).and_return(private_message_fixture, public_message_fixture)
44
+ fake_connection.stub(:get).with('/v1/events').and_return(fake_response)
45
+ client.stub(:register).and_return(fake_queue)
46
+
47
+ client.connection = fake_connection
48
+
49
+ messages = []
50
+ begin
51
+ client.stream_public_messages do |msg|
52
+ messages << msg
53
+ raise ExitStreamingGracefullyError if messages.count == 1
54
+ end
55
+ rescue ExitStreamingGracefullyError
56
+ messages.each { |msg| expect(msg.type).to eq("stream") }
57
+ end
58
+ end
59
+ end
60
+
61
+ describe "#stream_messages" do
62
+ it "streams both public and private messages" do
63
+ fake_response.stub(:body).and_return(public_message_fixture, private_message_fixture)
64
+ fake_connection.stub(:get).with('/v1/events').and_return(fake_response)
65
+ client.stub(:register).and_return(fake_queue)
66
+
67
+ client.connection = fake_connection
68
+
69
+ messages = []
70
+ begin
71
+ client.stream_messages do |msg|
72
+ messages << msg
73
+ raise ExitStreamingGracefullyError if messages.count == 2
74
+ end
75
+ rescue ExitStreamingGracefullyError
76
+ expect(messages.first.type).to eq "stream"
77
+ expect(messages.last.type).to eq "private"
78
+ end
79
+ end
80
+ end
81
+
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,64 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ module Zulip
5
+ class Client
6
+ describe Messages do
7
+ it "posts a message to a stream" do
8
+ subject = "sending some internet websites to the internet"
9
+ content = "Hey, I found this on the internet: http://nbapaintings.blogspot.com/"
10
+ stream = "test-stream"
11
+
12
+ client = Zulip::Client.new
13
+ fake_response = fixture("post-message-to-stream.json")
14
+ fake_connection = double("fake connection", post: fake_response )
15
+ client.connection = fake_connection
16
+
17
+ fake_connection.should_receive(:params=).with({"type"=>"stream", "content"=>content,
18
+ "to" => json_encode_list(stream),
19
+ "subject"=>subject})
20
+
21
+ fake_connection.should_receive(:post).with("v1/messages")
22
+ client.send_message(subject, content, stream)
23
+ end
24
+
25
+ it "sends private messages to a single user" do
26
+ content = "Hi, I heard you like the internet. Here: http://en.wikipedia.org/wiki/Zebroid"
27
+ recipient = "internet_cat@example.com"
28
+
29
+ client = Zulip::Client.new
30
+
31
+ fake_response = fixture("sending-private-message-success.json")
32
+ fake_connection = double("fake connection", post: fake_response )
33
+ client.connection = fake_connection
34
+
35
+ fake_connection.should_receive(:params=).with({"type"=>"private", "content"=>content,
36
+ "to" => json_encode_list(recipient)})
37
+ fake_connection.should_receive(:post).with("v1/messages")
38
+ client.send_private_message(content, recipient)
39
+ end
40
+
41
+ it "sends private messages to multiple users" do
42
+
43
+ content = 'testing private messages to multiple users'
44
+ recipients = ["bob@gmail.com", "alice@gmail.com"]
45
+
46
+ client = Zulip::Client.new
47
+
48
+ fake_response = fixture("sending-private-message-success.json")
49
+ fake_connection = double("fake connection", post: fake_response )
50
+ client.connection = fake_connection
51
+
52
+ fake_connection.should_receive(:params=).with({"type"=>"private", "content"=>content,
53
+ "to" => json_encode_list(recipients)})
54
+ fake_connection.should_receive(:post).with("v1/messages")
55
+
56
+ client.send_private_message(content, recipients)
57
+ end
58
+
59
+ def json_encode_list(item)
60
+ JSON.generate(Array(item).flatten)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ module Zulip
5
+ class Client
6
+ describe QueueRegistration do
7
+ describe "registering a queue to stream messages" do
8
+ context "with an event type of messages" do
9
+ it "posts to the register api" do
10
+ fake_connection = double("fake connection")
11
+ fake_response = double("response", body: fixture("queue-registration-success.json"))
12
+ fake_connection.stub(:post).with("v1/register").and_return(fake_response)
13
+
14
+ client = Zulip::Client.new
15
+ client.connection = fake_connection
16
+
17
+ fake_connection.should_receive(:params=).with( { "event_types" => '["message"]' } )
18
+ fake_connection.should_receive(:post).with("v1/register")
19
+
20
+ stream_type = :message
21
+ client.register(stream_type)
22
+ end
23
+
24
+ it "returns an object with the queue's id and last event id" do
25
+ fake_response = double("response", body: fixture("queue-registration-success.json"))
26
+ fake_connection = double("fake connection", :params= => nil )
27
+ fake_connection.stub(:post).with("v1/register").and_return(fake_response)
28
+
29
+ client = Zulip::Client.new
30
+ client.connection = fake_connection
31
+
32
+ the_expected_queue_id = "1384438093:1161"
33
+ the_expected_last_event_id = -1
34
+ stream_type = :message
35
+ response = client.register(stream_type)
36
+
37
+ expect(response.queue_id).to eq the_expected_queue_id
38
+ expect(response.last_event_id).to eq the_expected_last_event_id
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ module Zulip
5
+ class Client
6
+ describe StreamSubscriptions do
7
+ describe "#get_subscriptions" do
8
+ it "returns the bot's stream subscriptions" do
9
+ fake_http_response = double("fake http response",
10
+ body: fixture("get-user-subscriptions.json"))
11
+ fake_connection = double("fake connection", get: fake_http_response)
12
+
13
+ client = Zulip::Client.new
14
+ client.connection = fake_connection
15
+
16
+ subscriptions = client.get_subscriptions
17
+ expect(subscriptions.first).to be_a Zulip::StreamSubscription
18
+ expect(subscriptions.last).to be_a Zulip::StreamSubscription
19
+ end
20
+ end
21
+
22
+ describe "#subscribe" do
23
+ it "sends a PATCH message to subscribe the user to a stream" do
24
+ fake_http_response = double("fake response",
25
+ body: fixture("add-subscription.json"))
26
+ fake_connection = double("fake connection", patch: fake_http_response)
27
+
28
+ client = Zulip::Client.new
29
+ client.connection = fake_connection
30
+
31
+ expected_params = ["v1/users/me/subscriptions",
32
+ {:add=>"[{\"name\":\"clojure\"},{\"name\":\"ruby\"}]"}]
33
+ fake_connection.should_receive(:patch).with(*expected_params)
34
+
35
+ streams = ["clojure", "ruby"]
36
+ client.subscribe(streams)
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+ require_relative "../../../../lib/zulip"
3
+
4
+ module Zulip
5
+ class Client
6
+ describe Users do
7
+ describe "get_users" do
8
+ it "returns users in the bot's realm" do
9
+
10
+ fake_http_response = double("fake http response", body: fixture("get-users.json"))
11
+ fake_connection = double("fake connection", get: fake_http_response)
12
+
13
+ client = Zulip::Client.new
14
+ client.connection = fake_connection
15
+
16
+ users = client.get_users
17
+ expect(users.first).to be_kind_of Zulip::User
18
+ expect(users.last).to be_kind_of Zulip::User
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ require_relative "../../../lib/zulip"
2
+
3
+ describe Zulip::Client do
4
+ describe "configuration" do
5
+ it "yields itself upon initialization for configuration" do
6
+
7
+ client = Zulip::Client.new do |config|
8
+ config.bot_email_address = "bot@example.com"
9
+ config.api_key = "apikey"
10
+ end
11
+
12
+ expect(client.bot_email_address).to eq "bot@example.com"
13
+ expect(client.api_key).to eq "apikey"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,79 @@
1
+ require_relative "../../../lib/zulip"
2
+
3
+ module Zulip
4
+ describe Message do
5
+ let(:message_data) { {
6
+ "recipient_id"=>23442,
7
+ "sender_email"=>"ryanvergeront@gmail.com",
8
+ "timestamp"=>1384460690,
9
+ "display_recipient"=>"test-stream",
10
+ "sender_id"=>2874,
11
+ "sender_full_name"=>"Ryan Vergeront (F'13)",
12
+ "sender_domain"=>"students.hackerschool.com",
13
+ "content"=>"register an event type of 'message' not 'messages'",
14
+ "gravatar_hash"=>"a8e2e659c929c79669f97603bd73fcc5",
15
+ "avatar_url"=>
16
+ "https://secure.gravatar.com/avatar/a8e2e659c929c79669f97603bd73fcc5?d=identicon",
17
+ "client"=>"desktop app Mac 0.3.10",
18
+ "content_type"=>"text/x-markdown",
19
+ "subject_links"=>[],
20
+ "sender_short_name"=>"ryanvergeront",
21
+ "type"=>"stream",
22
+ "id"=>12791579,
23
+ "subject"=>"sending some internet websites to the internet" } }
24
+
25
+ it "implements attr_readers for it's attributes" do
26
+ message = Message.new(message_data)
27
+ expect(message.sender_email).to eq "ryanvergeront@gmail.com"
28
+
29
+ message_data.keys.each do |name|
30
+ return_value = message.send(name.to_sym)
31
+ expect(return_value).not_to be_nil
32
+ end
33
+ expect(message.stream).to eq "test-stream"
34
+ end
35
+
36
+ let(:pm_data) { {"recipient_id"=>25104,
37
+ "sender_email"=>"test_zulip_cli-bot@students.hackerschool.com",
38
+ "timestamp"=>1384536182,
39
+ "display_recipient"=>
40
+ [{"domain"=>"students.hackerschool.com",
41
+ "short_name"=>"ryanvergeront",
42
+ "email"=>"ryanvergeront@gmail.com",
43
+ "full_name"=>"Ryan Vergeront (F'13)",
44
+ "id"=>2874},
45
+ {"full_name"=>"Ryan's Bot",
46
+ "domain"=>"students.hackerschool.com",
47
+ "email"=>"test_zulip_cli-bot@students.hackerschool.com",
48
+ "short_name"=>"test_zulip_cli-bot",
49
+ "id"=>2934}],
50
+ "sender_id"=>2934,
51
+ "sender_full_name"=>"Ryan's Bot",
52
+ "sender_domain"=>"students.hackerschool.com",
53
+ "content"=>
54
+ "Ryan Vergeront (F'13), posted to test-stream:\n sending some internet websites to the internet: :)",
55
+ "gravatar_hash"=>"9edc706c9ea089025a4bede65630866b",
56
+ "avatar_url"=>
57
+ "https://secure.gravatar.com/avatar/9edc706c9ea089025a4bede65630866b?d=identicon",
58
+ "client"=>"API",
59
+ "content_type"=>"text/x-markdown",
60
+ "subject_links"=>[],
61
+ "sender_short_name"=>"test_zulip_cli-bot",
62
+ "type"=>"private",
63
+ "id"=>12836310,
64
+ "subject"=>""} }
65
+
66
+ it "works for private messages" do
67
+ pm = Message.new(pm_data)
68
+ expect(pm.display_recipients).to be_a(Array)
69
+
70
+ pm_data.each do |name, value|
71
+ unless value.class == Array
72
+ return_value = pm.send(name.to_sym)
73
+ expect(return_value).not_to be_nil
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "../../../lib/zulip"
2
+
3
+ module Zulip
4
+ describe StreamSubscription do
5
+ let(:data) { { "name"=>"VIM",
6
+ "color"=>"#76ce90",
7
+ "notifications"=>false,
8
+ "subscribers"=>["bob@example.com", "alice@example.com"],
9
+ "invite_only"=>false,
10
+ "email_address"=>"VIM@streams.zulip.com",
11
+ "in_home_view"=>true } }
12
+
13
+ it "has attrs" do
14
+ subscription = StreamSubscription.new(data)
15
+ data.each do |key, value|
16
+ expect(subscription.send(key)).to eq value
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "../../../lib/zulip"
2
+
3
+ module Zulip
4
+ describe User do
5
+ it "has an email" do
6
+ data = {"email"=>"bob@example.org","full_name"=>"Bob G"}
7
+ user = User.new(data)
8
+ expect(user.email).to eq "bob@example.org"
9
+ end
10
+
11
+ it "has a full name" do
12
+ data = {"email"=>"bob@example.org","full_name"=>"Bob G"}
13
+ user = User.new(data)
14
+ expect(user.full_name).to eq "Bob G"
15
+ end
16
+ end
17
+ end
data/zulip-rb.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'zulip/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "zulip"
8
+ spec.version = Zulip::VERSION
9
+ spec.authors = ["Ryan Vergeront"]
10
+ spec.email = ["ryan.vergeront@gmail.com"]
11
+ spec.description = %q{A ruby wrapper for the Zulip API.}
12
+ spec.summary = spec.description
13
+ spec.homepage = "https://github.com/verg/zulip-rb"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency('faraday', ['>= 0.7', '< 0.9'])
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency('rspec', '~> 2.4')
25
+ spec.add_development_dependency "pry"
26
+
27
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zulip
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Vergeront
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0.7'
20
+ - - <
21
+ - !ruby/object:Gem::Version
22
+ version: '0.9'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0.7'
30
+ - - <
31
+ - !ruby/object:Gem::Version
32
+ version: '0.9'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: '1.3'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '1.3'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: '2.4'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ~>
73
+ - !ruby/object:Gem::Version
74
+ version: '2.4'
75
+ - !ruby/object:Gem::Dependency
76
+ name: pry
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description: A ruby wrapper for the Zulip API.
90
+ email:
91
+ - ryan.vergeront@gmail.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - .gitignore
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - example/zulip_message_echo.rb
102
+ - lib/zulip.rb
103
+ - lib/zulip/client.rb
104
+ - lib/zulip/client/event_parser.rb
105
+ - lib/zulip/client/event_streaming.rb
106
+ - lib/zulip/client/messages.rb
107
+ - lib/zulip/client/queue_registration.rb
108
+ - lib/zulip/client/stream_subscriptions.rb
109
+ - lib/zulip/client/users.rb
110
+ - lib/zulip/message.rb
111
+ - lib/zulip/stream_subscription.rb
112
+ - lib/zulip/user.rb
113
+ - lib/zulip/version.rb
114
+ - spec/fixtures/add-subscription.json
115
+ - spec/fixtures/get-message-event-success.json
116
+ - spec/fixtures/get-private-message.json
117
+ - spec/fixtures/get-user-subscriptions.json
118
+ - spec/fixtures/get-users.json
119
+ - spec/fixtures/post-message-to-stream.json
120
+ - spec/fixtures/queue-registration-success.json
121
+ - spec/fixtures/sending-private-message-success.json
122
+ - spec/helper.rb
123
+ - spec/lib/zulip/client/event_parser_spec.rb
124
+ - spec/lib/zulip/client/event_streaming_spec.rb
125
+ - spec/lib/zulip/client/messages_spec.rb
126
+ - spec/lib/zulip/client/queue_registration_spec.rb
127
+ - spec/lib/zulip/client/stream_subscriptions_spec.rb
128
+ - spec/lib/zulip/client/users_spec.rb
129
+ - spec/lib/zulip/client_spec.rb
130
+ - spec/lib/zulip/message_spec.rb
131
+ - spec/lib/zulip/stream_subscription_spec.rb
132
+ - spec/lib/zulip/user_spec.rb
133
+ - zulip-rb.gemspec
134
+ homepage: https://github.com/verg/zulip-rb
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.0.5
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: A ruby wrapper for the Zulip API.
158
+ test_files:
159
+ - spec/fixtures/add-subscription.json
160
+ - spec/fixtures/get-message-event-success.json
161
+ - spec/fixtures/get-private-message.json
162
+ - spec/fixtures/get-user-subscriptions.json
163
+ - spec/fixtures/get-users.json
164
+ - spec/fixtures/post-message-to-stream.json
165
+ - spec/fixtures/queue-registration-success.json
166
+ - spec/fixtures/sending-private-message-success.json
167
+ - spec/helper.rb
168
+ - spec/lib/zulip/client/event_parser_spec.rb
169
+ - spec/lib/zulip/client/event_streaming_spec.rb
170
+ - spec/lib/zulip/client/messages_spec.rb
171
+ - spec/lib/zulip/client/queue_registration_spec.rb
172
+ - spec/lib/zulip/client/stream_subscriptions_spec.rb
173
+ - spec/lib/zulip/client/users_spec.rb
174
+ - spec/lib/zulip/client_spec.rb
175
+ - spec/lib/zulip/message_spec.rb
176
+ - spec/lib/zulip/stream_subscription_spec.rb
177
+ - spec/lib/zulip/user_spec.rb