├── .gitignore ├── Gemfile ├── README.md ├── Rakefile ├── shot.png ├── slacklog.rb └── spec ├── slacklog_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "rake" 4 | gem "rspec" 5 | gem "webmock" 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **ARCHIVED**: This project has been archived. The original authors no longer use it 2 | and Slack is currently phasing out the API it relies on. 3 | 4 | # Slacklog 5 | 6 | Pull [Slack][] chat history in when opening a new buffer. 7 | 8 | [slack]: https://slack.com/ 9 | 10 | ![shot](shot.png) 11 | 12 | ## Installation 13 | 14 | This script is not yet released, please install manually: 15 | 16 | ``` 17 | % curl -o ~/.weechat/ruby/slacklog.rb \ 18 | "https://raw.githubusercontent.com/thoughtbot/weechat-slacklog/master/slacklog.rb" 19 | ``` 20 | 21 | Optionally set the script to autoload when WeeChat starts: 22 | 23 | ``` 24 | % cd ~/.weechat/ruby/autoload && ln -s ../slacklog.rb . 25 | ``` 26 | 27 | Restart WeeChat or load the script manually: 28 | 29 | ``` 30 | /script load slacklog.rb 31 | ``` 32 | 33 | ## Required Settings 34 | 35 | **Note**: You can generate an API token [here][docs]. 36 | 37 | ``` 38 | /set plugins.var.ruby.slacklog.servers "thoughtbot" 39 | /set plugins.var.ruby.slacklog.thoughtbot.api_token "abc-123" 40 | ``` 41 | 42 | These variables can also be modified via the `/slacklog` command: 43 | 44 | ``` 45 | /slacklog add thoughtbot abc-123 46 | ``` 47 | 48 | [docs]: https://api.slack.com/web#authentication 49 | 50 | ## Optional Settings 51 | 52 | By default, the Slack API returns 100 messages when no count is specified. You 53 | can change this by setting a value between 1 and 1000: 54 | 55 | ``` 56 | /set plugins.var.ruby.slacklog.count 500 57 | ``` 58 | 59 | To change the color of Slacklog lines, change the following WeeChat setting: 60 | 61 | ``` 62 | /set logger.color.backlog_line darkgray 63 | ``` 64 | 65 | ## Usage 66 | 67 | Whenever you open a buffer for a server in your `servers` list (and which has a 68 | token defined), 100 messages of history will be fetched via the Slack API and 69 | printed into the buffer. 70 | 71 | The `/slacklog` command can be used without arguments to actively fetch and 72 | print history for an already open buffer. 73 | 74 | ## Implementation Details 75 | 76 | The script can be used outside of WeeChat like so: 77 | 78 | ``` 79 | % ruby ./slacklog.rb fetch API-TOKEN CHANNEL 80 | ``` 81 | 82 | This prints history messages on `stdout` and has no dependencies on the 83 | `Weechat` module. The implementation is contained in a normal `SlackAPI` class 84 | which is well tested. 85 | 86 | Global functions wire this up as a WeeChat plugin in the following way: 87 | 88 | - Whenever a channel is joined or the `/slacklog` command is invoked, we 89 | determine the token and channel then ask `Weechat` to asynchronously execute 90 | our script as above with a callback. 91 | - The callback will receive the lines output by our script, and print them into 92 | the buffer. 93 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | 3 | RSpec::Core::RakeTask.new { |t| t.rspec_opts = "--color" } 4 | 5 | task default: :spec 6 | -------------------------------------------------------------------------------- /shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoughtbot/weechat-slacklog/ccc5e88141fc3947ad14c5b2682c962f4fdd8eeb/shot.png -------------------------------------------------------------------------------- /slacklog.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Pat Brisbin 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | # 21 | # Fetch Slack chat history when opening a buffer. 22 | # 23 | # Required settings: 24 | # 25 | # - plugins.var.ruby.slacklog.servers "foo,bar" 26 | # - plugins.var.ruby.slacklog.foo.api_token "foo-api-token" 27 | # - plugins.var.ruby.slacklog.bar.api_token "bar-api-token" 28 | # 29 | ### 30 | require "cgi" 31 | require "json" 32 | require "net/https" 33 | require "uri" 34 | 35 | SCRIPT_NAME = "slacklog" 36 | SCRIPT_AUTHOR = "Pat Brisbin " 37 | SCRIPT_VERSION = "0.1" 38 | SCRIPT_LICENSE = "MIT" 39 | SCRIPT_DESCRIPTION = "Slack backlog" 40 | SCRIPT_FILE = File.join(ENV["HOME"], ".weechat", "ruby", "#{SCRIPT_NAME}.rb") 41 | 42 | NAMESPACE = "plugins.var.ruby.#{SCRIPT_NAME}" 43 | API_TOKENS = {} 44 | 45 | class SlackAPI 46 | BASE_URL = "https://slack.com/api" 47 | 48 | def initialize(token) 49 | @token = token 50 | end 51 | 52 | def backlog(name, count = nil) 53 | name = name.sub(/^#/, "") 54 | members = rpc("users.list").fetch("members") 55 | 56 | history(name, count).reverse.flat_map do |message| 57 | user = message.key?("user") && 58 | members.detect { |u| u["id"] == message["user"] } 59 | 60 | format_message_lines(user, message["text"], members) 61 | end 62 | end 63 | 64 | private 65 | 66 | def history(name, count = nil) 67 | channels = rpc("channels.list").fetch("channels") 68 | 69 | if channel = channels.detect { |c| c["name"] == name } 70 | return rpc("channels.history", history_params(channel, count)).fetch("messages") 71 | end 72 | 73 | groups = rpc("groups.list").fetch("groups") 74 | 75 | if group = groups.detect { |g| g["name"] == name } 76 | return rpc("groups.history", history_params(group, count)).fetch("messages") 77 | end 78 | 79 | users = rpc("users.list").fetch("members") 80 | 81 | if user = users.detect { |g| g["name"] == name } 82 | ims = rpc("im.list").fetch("ims") 83 | if im = ims.detect { |g| g["user"] == user["id"] } 84 | return rpc("conversations.history", history_params(im, count)).fetch("messages") 85 | end 86 | end 87 | 88 | [] 89 | end 90 | 91 | def history_params(room, count) 92 | params = { channel: room["id"] } 93 | 94 | if count 95 | params[:count] = count 96 | end 97 | 98 | params 99 | end 100 | 101 | def format_message_lines(user, body, members) 102 | return [] unless user 103 | 104 | body.lines.map do |line| 105 | fixed = fix_join_parts(fix_usernames(line.chomp, members)) 106 | 107 | "#{user["name"]}\t#{CGI.unescapeHTML(fixed)}" 108 | end 109 | end 110 | 111 | def fix_join_parts(line) 112 | line.gsub(/<@.*?\|(.*?)>/, '\1') 113 | end 114 | 115 | def fix_usernames(line, members) 116 | line.gsub(/<@(.*?)>/) do |match| 117 | if user = members.detect { |u| u["id"] == $1 } 118 | user["name"] 119 | else 120 | match 121 | end 122 | end 123 | end 124 | 125 | def rpc(method, arguments = {}) 126 | params = parameterize({ token: @token }.merge(arguments)) 127 | uri = URI.parse("#{BASE_URL}/#{method}?#{params}") 128 | response = Net::HTTP.start(uri.host, use_ssl: true) do |http| 129 | http.get(uri.request_uri) 130 | end 131 | 132 | JSON.parse(response.body).tap do |result| 133 | result["ok"] or raise "API Error: #{result.inspect}" 134 | end 135 | rescue JSON::ParserError 136 | raise "API Error: unable to parse HTTP response" 137 | end 138 | 139 | def parameterize(query) 140 | query.map { |k,v| "#{escape(k)}=#{escape(v)}" }.join("&") 141 | end 142 | 143 | def escape(value) 144 | URI.escape(value.to_s) 145 | end 146 | end 147 | 148 | def output_history(buffer_id) 149 | server, name = Weechat.buffer_get_string(buffer_id, "name").split('.') 150 | 151 | if token = API_TOKENS[server] 152 | count = Weechat.config_get_plugin("count") 153 | run_script = "ruby '#{SCRIPT_FILE}' fetch '#{token}' '#{name}' #{count}" 154 | Weechat.hook_process(run_script, 0, "on_process_complete", buffer_id) 155 | else 156 | Weechat.print("", "slacklog warn: no token for server '#{server}' found!") 157 | end 158 | 159 | Weechat::WEECHAT_RC_OK 160 | end 161 | 162 | def on_slacklog(_, buffer_id, args) 163 | if /^add (?[^ ]*) (?[^ ]*)$/ =~ args 164 | server_list = Weechat.config_get_plugin("servers") 165 | Weechat.config_set_plugin("servers", "#{server_list},#{server}") 166 | Weechat.config_set_plugin("#{server}.api_token", token) 167 | 168 | read_tokens 169 | elsif /^remove (?[^ ]*)$/ =~ args 170 | server_list = Weechat.config_get_plugin("servers") 171 | server_list = server_list.split(',').reject { |s| s == server }.join(',') 172 | Weechat.config_set_plugin("servers", server_list) 173 | Weechat.config_unset_plugin("#{server}.api_token") 174 | 175 | read_tokens 176 | else 177 | output_history(buffer_id) 178 | end 179 | end 180 | 181 | def on_join(_, signal, data) 182 | server = signal.split(",").first 183 | nick = Weechat.info_get("irc_nick", server) 184 | joined_nick = Weechat.info_get("irc_nick_from_host", data) 185 | 186 | if joined_nick != nick 187 | # Not our own JOIN event 188 | return Weechat::WEECHAT_RC_OK 189 | end 190 | 191 | channel = data.sub(/.*JOIN (.*)$/, '\1') 192 | buffer_id = Weechat.info_get("irc_buffer", "#{server},#{channel}") 193 | 194 | output_history(buffer_id) 195 | end 196 | 197 | def on_process_complete(buffer_id, _, rc, out, err) 198 | if rc.to_i == 0 199 | fg = Weechat.config_color(Weechat.config_get("logger.color.backlog_line")) 200 | color = Weechat.color("#{fg},default}") 201 | 202 | out.lines do |line| 203 | nick, text = line.strip.split("\t") 204 | Weechat.print(buffer_id, "%s%s\t%s%s" % [color, nick, color, text]) 205 | end 206 | end 207 | 208 | if rc.to_i > 0 209 | err.lines do |line| 210 | Weechat.print("", "slacklog error: #{line.strip}") 211 | end 212 | end 213 | 214 | Weechat::WEECHAT_RC_OK 215 | end 216 | 217 | def read_tokens(*) 218 | server_list = Weechat.config_get_plugin("servers") 219 | server_list.split(",").map(&:strip).each do |server| 220 | api_token = Weechat.config_get_plugin("#{server}.api_token") 221 | if api_token.start_with?('${sec.data.') 222 | api_token = Weechat.string_eval_expression(api_token, {}, {}, {}) 223 | end 224 | API_TOKENS[server] = api_token 225 | end 226 | 227 | Weechat::WEECHAT_RC_OK 228 | end 229 | 230 | def weechat_init 231 | Weechat.register( 232 | SCRIPT_NAME, 233 | SCRIPT_AUTHOR, 234 | SCRIPT_VERSION, 235 | SCRIPT_LICENSE, 236 | SCRIPT_DESCRIPTION, 237 | "", "" 238 | ) 239 | 240 | Weechat.hook_config("#{NAMESPACE}.*", "read_tokens", "") 241 | Weechat.hook_signal("*,irc_in2_join", "on_join", "") 242 | 243 | Weechat.hook_command( 244 | "slacklog", 245 | "manage servers, or print history in current buffer", 246 | "[add server api-token | remove server |]", 247 | "", "", "on_slacklog", "" 248 | ) 249 | 250 | read_tokens 251 | end 252 | 253 | if ARGV.shift == "fetch" 254 | token, room_name, count = ARGV 255 | 256 | slack_api = SlackAPI.new(token) 257 | slack_api.backlog(room_name, count).each { |line| puts line } 258 | end 259 | -------------------------------------------------------------------------------- /spec/slacklog_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | context "slacklog.rb" do 4 | context SlackAPI do 5 | let(:token) { "api-token" } 6 | 7 | it "raises when appropriate" do 8 | api = SlackAPI.new(token) 9 | mock_slack_api("users.list?token=#{token}" => { ok: false }) 10 | 11 | expect { api.backlog("#general") }.to raise_error(/API Error/) 12 | end 13 | 14 | context "with thoughtbot's rooms" do 15 | before do 16 | mock_slack_api( 17 | "users.list?token=#{token}" => { 18 | ok: true, 19 | members: [ 20 | { id: "1", name: "adarsh" }, 21 | { id: "2", name: "joe" }, 22 | ] 23 | }, 24 | "channels.list?token=#{token}" => { 25 | ok: true, 26 | channels: [{ id: "123", name: "general" }] 27 | }, 28 | "groups.list?token=#{token}" => { 29 | ok: true, 30 | groups: [{ id: "456", name: "dev" }] 31 | }, 32 | "channels.history?token=#{token}&channel=123" => { 33 | ok: true, 34 | messages: [ 35 | { user: "1", text: "hello" }, 36 | { user: "2", text: "good bye" }, 37 | ] 38 | }, 39 | "groups.history?token=#{token}&channel=456" => { 40 | ok: true, 41 | messages: [ 42 | { user: "1", text: "see ya" }, 43 | { user: "2", text: "later" }, 44 | ] 45 | }, 46 | ) 47 | end 48 | 49 | it "finds the backlog for the #general channel" do 50 | api = SlackAPI.new(token) 51 | 52 | backlog = api.backlog("#general") 53 | 54 | expect(backlog).to match_array ["adarsh\thello", "joe\tgood bye"] 55 | end 56 | 57 | it "finds the backlog for the #dev group" do 58 | api = SlackAPI.new(token) 59 | 60 | backlog = api.backlog("#dev") 61 | 62 | expect(backlog).to match_array ["adarsh\tsee ya", "joe\tlater"] 63 | end 64 | end 65 | 66 | it "handles multi-line messages correctly" do 67 | api = SlackAPI.new(token) 68 | message = %{``` 69 | m = MyModel.new(whatever: 'foo') 70 | m.whatever 71 | m.my_hstore 72 | ```} 73 | mock_history_message("general", "pat", message) 74 | 75 | backlog = api.backlog("#general") 76 | 77 | expect(backlog).to eq [ 78 | "pat\t```", 79 | "pat\tm = MyModel.new(whatever: 'foo')", 80 | "pat\tm.whatever", 81 | "pat\tm.my_hstore", 82 | "pat\t```", 83 | ] 84 | end 85 | 86 | it "replaces userids with usernames if available" do 87 | api = SlackAPI.new(token) 88 | mock_history_message("general", "pat", "Hey <@2> and <@3> and <@4>", [ 89 | { id: "2", name: "adarsh" }, 90 | { id: "3", name: "jferris" } 91 | ]) 92 | 93 | backlog = api.backlog("#general") 94 | 95 | expect(backlog).to eq ["pat\tHey adarsh and jferris and <@4>"] 96 | end 97 | 98 | it "fixes join/part usernames" do 99 | api = SlackAPI.new(token) 100 | mock_history_message("general", "pat", "<@3|pat> has joined") 101 | 102 | backlog = api.backlog("#general") 103 | 104 | expect(backlog).to eq ["pat\tpat has joined"] 105 | end 106 | 107 | it "de-escapes HTML entities" do 108 | api = SlackAPI.new(token) 109 | mock_history_message("general", "pat", "My code > your code") 110 | 111 | backlog = api.backlog("#general") 112 | 113 | expect(backlog).to eq ["pat\tMy code > your code"] 114 | end 115 | 116 | it "can have the number of messages to display configured" do 117 | api = SlackAPI.new(token) 118 | mock_slack_api( 119 | "users.list?token=#{token}" => { 120 | ok: true, members: [{ id: "1", name: "pat" }] 121 | }, 122 | "channels.list?token=#{token}" => { 123 | ok: true, channels: [{ id: "123", name: "general" }] 124 | }, 125 | "channels.history?token=#{token}&channel=123&count=2" => { 126 | ok: true, messages: [ 127 | { user: "1", text: "One" }, 128 | { user: "1", text: "Two" }, 129 | ] 130 | }, 131 | ) 132 | 133 | backlog = api.backlog("#general", 2) 134 | 135 | expect(backlog).to eq ["pat\tTwo", "pat\tOne"] 136 | end 137 | 138 | def mock_history_message(channel, user, message, members = []) 139 | mock_slack_api( 140 | "users.list?token=#{token}" => { 141 | ok: true, members: [{ id: "1", name: user }] + members 142 | }, 143 | "channels.list?token=#{token}" => { 144 | ok: true, channels: [{ id: "123", name: channel }] 145 | }, 146 | "channels.history?token=#{token}&channel=123" => { 147 | ok: true, messages: [{ user: "1", text: message }] 148 | }, 149 | ) 150 | end 151 | 152 | def mock_slack_api(requests) 153 | requests.each do |path, response| 154 | stub_request(:get, "#{SlackAPI::BASE_URL}/#{path}"). 155 | to_return(body: response.to_json) 156 | end 157 | end 158 | end 159 | 160 | context "on_join" do 161 | it "spawns the script if it's us joining and we have a token" do 162 | API_TOKENS["foo"] = "api-token" 163 | simulate_nick("foo", "pbrisbin") 164 | simulate_buffers("1" => "foo.#bar") 165 | 166 | simulate_join("1", "foo", "#bar", "pbrisbin") 167 | 168 | expect(Weechat).to have_received(:hook_process). 169 | with( 170 | "ruby '#{SCRIPT_FILE}' fetch 'api-token' '#bar' ", 171 | 0, 172 | "on_process_complete", 173 | "1" 174 | ) 175 | end 176 | 177 | it "includes count if configured" do 178 | API_TOKENS["foo"] = "api-token" 179 | simulate_nick("foo", "pbrisbin") 180 | simulate_buffers("1" => "foo.#bar") 181 | allow(Weechat).to receive(:config_get_plugin).with("count").and_return("10") 182 | 183 | simulate_join("1", "foo", "#bar", "pbrisbin") 184 | 185 | expect(Weechat).to have_received(:hook_process). 186 | with( 187 | "ruby '#{SCRIPT_FILE}' fetch 'api-token' '#bar' 10", 188 | 0, 189 | "on_process_complete", 190 | "1" 191 | ) 192 | end 193 | 194 | it "does nothing if it's not our nick" do 195 | simulate_nick("foo", "pbrisbin") 196 | 197 | simulate_join("1", "foo", "#bar", "jferris") 198 | 199 | expect(Weechat).not_to have_received(:hook_process) 200 | end 201 | 202 | it "does nothing if we have no token" do 203 | simulate_nick("foo", "pbrisbin") 204 | simulate_buffers("1" => "foo.#bar") 205 | 206 | simulate_join("1", "foo", "#bar", "pbrisbin") 207 | 208 | expect(Weechat).not_to have_received(:hook_process) 209 | end 210 | 211 | def simulate_nick(server, nick) 212 | allow(Weechat).to receive("info_get"). 213 | with("irc_nick", server).and_return(nick) 214 | end 215 | 216 | def simulate_buffers(buffers) 217 | buffers.each do |buffer_id, buffer_name| 218 | allow(Weechat).to receive(:buffer_get_string). 219 | with(buffer_id, "name").and_return(buffer_name) 220 | end 221 | end 222 | 223 | def simulate_join(buffer_id, server, channel, nick) 224 | signal = "#{server},irc_in2_JOIN" 225 | data = ":#{nick}!#{nick}@example.com JOIN #{channel}" 226 | 227 | allow(Weechat).to receive("info_get"). 228 | with("irc_nick_from_host", data).and_return(nick) 229 | allow(Weechat).to receive("info_get"). 230 | with("irc_buffer", "#{server},#{channel}").and_return(buffer_id) 231 | 232 | on_join("", signal, data) 233 | end 234 | end 235 | 236 | context "on_process_complete" do 237 | it "prints colorized history on success" do 238 | out = "foo\tbar\nbaz\tbat\n" 239 | allow(Weechat).to receive(:config_get) 240 | allow(Weechat).to receive(:config_color) 241 | allow(Weechat).to receive(:color).and_return("C") 242 | 243 | on_process_complete("1", nil, 0, out, nil) 244 | 245 | expect(Weechat).to have_received(:print).with("1", "Cfoo\tCbar") 246 | expect(Weechat).to have_received(:print).with("1", "Cbaz\tCbat") 247 | end 248 | 249 | it "prints error messages when unsuccessful" do 250 | err = "foo\nbar\nbaz\nbat\n" 251 | 252 | on_process_complete("1", nil, 127, nil, err) 253 | 254 | expect(Weechat).to have_received(:print).with("", "slacklog error: foo") 255 | expect(Weechat).to have_received(:print).with("", "slacklog error: bar") 256 | expect(Weechat).to have_received(:print).with("", "slacklog error: baz") 257 | expect(Weechat).to have_received(:print).with("", "slacklog error: bat") 258 | end 259 | 260 | it "does nothing if not finished" do 261 | on_process_complete(nil, nil, -1, nil, nil) 262 | 263 | expect(Weechat).not_to have_received(:print) 264 | end 265 | 266 | context "read_tokens" do 267 | it "reads weechat configuration into API_TOKENS" do 268 | allow(Weechat).to receive(:config_get_plugin). 269 | with("servers").and_return("foo,bar") 270 | allow(Weechat).to receive(:config_get_plugin). 271 | with("foo.api_token").and_return("foo-api-token") 272 | allow(Weechat).to receive(:config_get_plugin). 273 | with("bar.api_token").and_return("bar-api-token") 274 | 275 | read_tokens 276 | 277 | expect(API_TOKENS).to eq({ 278 | "foo" => "foo-api-token", 279 | "bar" => "bar-api-token", 280 | }) 281 | end 282 | 283 | context "weechat_init" do 284 | it "registers with Weechat and sets up hooks" do 285 | weechat_init 286 | 287 | expect(Weechat).to have_received(:register) 288 | expect(Weechat).to have_received(:hook_config) 289 | expect(Weechat).to have_received(:hook_signal) 290 | expect(Weechat).to have_received(:hook_command) 291 | end 292 | 293 | it "initializes tokens" do 294 | allow(Weechat).to receive(:config_get_plugin). 295 | with("servers").and_return("foo") 296 | 297 | weechat_init 298 | 299 | expect(API_TOKENS).to have_key("foo") 300 | end 301 | end 302 | end 303 | end 304 | end 305 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "rspec" 2 | require "webmock" 3 | require_relative "../slacklog" 4 | 5 | class Weechat 6 | WEECHAT_RC_OK = 1 7 | 8 | def self.print(*); end 9 | end 10 | 11 | RSpec.configure do |conf| 12 | conf.include(WebMock::API) 13 | 14 | conf.before(:each) do 15 | allow(Weechat).to receive(:config_get_plugin).and_return("") 16 | allow(Weechat).to receive(:hook_config) 17 | allow(Weechat).to receive(:hook_process) 18 | allow(Weechat).to receive(:hook_signal) 19 | allow(Weechat).to receive(:hook_command) 20 | allow(Weechat).to receive(:print) 21 | allow(Weechat).to receive(:register) 22 | end 23 | 24 | conf.after(:each) { API_TOKENS.clear } 25 | end 26 | --------------------------------------------------------------------------------