{"id":13480597,"url":"https://github.com/pewniak747/hipbot","last_synced_at":"2025-03-27T11:30:38.698Z","repository":{"id":3627929,"uuid":"4694204","full_name":"pewniak747/hipbot","owner":"pewniak747","description":"HipChat bot written in ruby and eventmachine","archived":false,"fork":false,"pushed_at":"2017-11-19T13:46:19.000Z","size":1748,"stargazers_count":87,"open_issues_count":3,"forks_count":7,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-20T22:04:34.546Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pewniak747.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-06-17T19:20:29.000Z","updated_at":"2022-03-30T14:10:16.000Z","dependencies_parsed_at":"2022-08-18T00:05:30.910Z","dependency_job_id":null,"html_url":"https://github.com/pewniak747/hipbot","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pewniak747%2Fhipbot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pewniak747%2Fhipbot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pewniak747%2Fhipbot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pewniak747%2Fhipbot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pewniak747","download_url":"https://codeload.github.com/pewniak747/hipbot/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245835888,"owners_count":20680287,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T17:00:42.146Z","updated_at":"2025-03-27T11:30:38.374Z","avatar_url":"https://github.com/pewniak747.png","language":"Ruby","funding_links":[],"categories":["Frameworks and libraries"],"sub_categories":["HipChat"],"readme":"# Hipbot\n\nHipbot is a XMPP bot for HipChat, written in Ruby with EventMachine.\n\n[![Build Status](https://secure.travis-ci.org/pewniak747/hipbot.png?branch=master)](http://travis-ci.org/pewniak747/hipbot)\n[![Code Climate](https://codeclimate.com/github/pewniak747/hipbot.png)](https://codeclimate.com/github/pewniak747/hipbot)\n[![Coverage Status](https://coveralls.io/repos/pewniak747/hipbot/badge.png?branch=master)](https://coveralls.io/r/pewniak747/hipbot)\n[![Dependency Status](https://gemnasium.com/pewniak747/hipbot.png)](https://gemnasium.com/pewniak747/hipbot)\n[![Gem Version](https://badge.fury.io/rb/hipbot.png)](http://badge.fury.io/rb/hipbot)\n\n### Compatibility\nHipbot is tested on:\n\n* Ruby 2.2, 2.3 and 2.4 series\n* JRuby (latest)\n* Rubinus (latest)\n\n### Dependencies\n\n* daemons \u003e= 1.1.8\n* activesupport \u003e= 3.2.12\n* eventmachine \u003e= 1.0.3\n* em-http-request \u003e= 1.0.3\n* xmpp4r ~\u003e 0.5\n\n## Getting started\n### Installation\n\n```shell\ngem install hipbot\n```\n\n### 1 minute setup on heroku\n\nFollow the instructions on [hipbot-example](https://github.com/netguru/hipbot-example).\n\n### Custom setup\nCreate `bot.rb` file, subclass `Hipbot::Bot` and customize the responses.\n\n```ruby\nrequire 'hipbot'\n\nclass MyBot \u003c Hipbot::Bot\n  configure do |c|\n    c.jid       = 'changeme@chat.hipchat.com'\n    c.password  = 'secret'\n  end\n\n  on /^hello$/ do\n    reply(\"Hello!\")\n  end\nend\n\nMyBot.start!\n```\n\n### Running\nStart Hipbot as a daemon by executing:\n\n```shell\nhipbot start\n```\n\nRun `hipbot` to see all available commands.\n\nStart in shell:\n\n```shell\nruby bot.rb\n```\n\n### Behavior\n* On start and runtime:\n    * Fetches details and presences of all users in Lobby\n    * Pings XMPP server every 60 seconds to keep alive\n* On new message:\n    * Invokes all matching reactions or falls back to default reaction\n\n## Usage\n### Configuration\nFull configuration example:\n```ruby\nclass MyBot \u003c Hipbot::Bot\n  configure do |c|\n    # Account JID (required) - see https://hipchat.com/account/xmpp for your JID\n    c.jid      = 'changeme@chat.hipchat.com'\n\n    # Account password (required)\n    c.password = 'secret'\n\n    # Custom helpers module (optional) - see below for examples\n    c.helpers  = MyHipbotHelpers\n\n    # Logger (default: Hipbot::Logger.new($stdout))\n    c.logger   = Hipbot::Logger.new($stdout)\n\n    # Initial status message (default: '')\n    c.status   = \"I'm here to help\"\n\n    # Storage adapter (default: Hipbot::Storages::Hash)\n    c.storage  = Hipbot::Storages::Hash\n\n    # Predefined room groups (optional)\n    c.rooms    = { project_rooms: ['Project 1', 'Project 2'] }\n\n    # Predefined user groups (optional)\n    c.teams    = { admins: ['John Smith'] }\n\n    # Auto join criteria (default: :all)\n    # Accepted values: :all, :public, :private, :none, \"room name\"\n    c.join     = :private\n\n    # Makes all reactions case insensitive (default: true)\n    c.case_insensitive = true\n\n    # Auto-join on invite (default: true)\n    c.join_on_invite = true\n  end\nend\n```\n### Reaction helpers\nInside the reaction block you have access to following context objects:\n\n* `bot`\n* `room`\n* `sender`\n* `message`\n* `reaction`\n\n### Joining rooms\nHipbot will join all accessible rooms by default on startup and invite.\n\nTo change auto join method use `join` configuration option:\n```ruby\nconfigure do |c|\n  # ...\n  c.join = :private\nend\n```\n```ruby\nconfigure do |c|\n  # ...\n  c.join = :none\nend\n```\n```ruby\nconfigure do |c|\n  # ...\n  c.join = ['Project Room', :public]\nend\n```\nNotice: Archived rooms are always ignored\n\n### Bot presence\nUse `bot.set_presence` method to change Hipbot presence:\n```ruby\non /^change status$/ do\n  bot.set_presence(\"Hello humans\")\nend\n```\n```ruby\non /^go away$/ do\n  bot.set_presence(\"I'm away\", :away)\nend\n```\n```ruby\non /^do not disturb$/ do\n  bot.set_presence(nil, :dnd)\nend\n```\n\n### Rooms\nUse `Hipbot::Room` for collection of available rooms.\n```ruby\non /^list all rooms$/ do\n  all_rooms = Hipbot::Room.all.map(\u0026:name)\n  reply(all_rooms.join(', '))\nend\n```\n```ruby\non /^get project room JID$/ do\n  project_room = Hipbot::Room.find_by(name: 'project room')\n  reply(project_room.id)\nend\n```\nUse `room` for current room object (it's `nil` if message is private):\n```ruby\non /^where am I\\?$/ do\n  reply(\n    \"You are in #{room}\\n\" +\n    \"JID: #{room.id}\\n\" +\n    \"Topic: #{room.topic}\\n\" +\n    \"Users online: #{room.users.count}\\n\" +\n    \"Privacy: #{room.privacy}\\n\" +\n    \"Hipchat ID: #{room.hipchat_id}\\n\" +\n    \"Archived?: #{room.archived? ? 'yes' : 'no'}\\n\" +\n    \"Guest URL: #{room.guest_url}\"\n  )\nend\n```\n\n### Users\nUse `Hipbot::User` for collection of all users:\n```ruby\non /^list all users$/ do\n  all_users = Hipbot::User.all.map(\u0026:name)\n  reply(all_users.join(', '))\nend\n```\n```ruby\non /^get John Smith's JID$/ do\n  john = Hipbot::Room.find_by(name: 'John Smith')\n  reply(john.id)\nend\n```\nUse `sender` for message sender object:\n```ruby\non /^who am I\\?$/ do\n  reply(\n    \"You are #{sender}\\n\" +\n    \"JID: #{sender.id}\\n\" +\n    \"Mention: @#{sender.mention}\\n\" +\n    \"E-mail: #{sender.email}\\n\" +\n    \"Title: #{sender.title}\\n\" +\n    \"Photo: #{sender.photo}\"\n  )\nend\n```\nUse `Room#users` method for online users array:\n```ruby\non /^list online users$/ do\n  reply room.users.map(\u0026:name).join(', ')\nend\n```\n\n### Replying\nUse `reply` method to send a message.\n\nReply in the same room / chat:\n```ruby\non /^hello$/ do\n  reply(\"Hello!\")\nend\n```\nReply in \"help room\":\n```ruby\non /^I need help$/ do\n  help_room = Hipbot::Room.find_by(name: 'help room')\n  reply(\"#{sender} needs help in #{room}\", help_room)\nend\n```\n\n### Private messaging\n```ruby\non /^send me private message$/ do\n  sender.send_message(\"Hello, #{sender}\")\nend\n```\n```ruby\non /^send private message to John$/ do\n  john = Hipbot::User.find_by(name: 'John Smith')\n  john.send_message(\"Hello, John!\")\nend\n```\n\n### Topics\n```ruby\non /^current topic$/ do\n  reply(\"Current topic: #{room.topic}\")\nend\n```\n```ruby\non /^change topic here$/ do\n  room.set_topic(\"New Topic\")\nend\n```\n```ruby\non /^change topic there$/ do\n  there = Hipbot::Room.find_by(name: 'there')\n  there.set_topic(\"New Topic\")\nend\n```\n\n### Regexp matchdata\n```ruby\non /^My name is (.*)$/ do |user_name|\n  reply(\"Hello, #{user_name}!\")\nend\n```\n```ruby\non /^My name is (\\S*) (\\S*)$/ do |first_name, last_name|\n  reply(\"Hello, #{first_name} #{last_name}!\")\nend\n```\n\n### Multiple regexps\n```ruby\non /^My name is (.*)$/, /^I am (.*)$/ do |user_name|\n  reply(\"Hello, #{user_name}!\")\nend\n```\n\n### Sender restriction\nUse `:from` option to match messages only from certain users or user groups defined in configuration.\nIt accepts string, symbol and array values.\n```ruby\nconfigure do |c|\n  # ...\n  c.teams = { vip: ['John Edward', 'Mike Anderson'] }\nend\n\non /^report status$/, from: ['Tom Smith', 'Jane Doe', :vip] do\n  reply('All clear')\nend\n```\n\n### Room restriction\nUse `:room` option to match messages opny from certain HipChat rooms.\nIt accepts string, symbol, array and boolean values.\n```ruby\nconfigure do |c|\n  # ...\n  c.rooms = { project_rooms: ['Project 1', 'Project 2'] }\nend\n\non /^hello$/, room: ['Public Room', :project_rooms] do\n  reply('Hello!')\nend\n```\nMatch only private messages:\n```ruby\non /^private hello$/, room: false do\n  reply('Private hello!')\nend\n```\nMatch only room messages:\n```ruby\non /^public hello$/, room: true do\n  reply('Public hello!')\nend\n```\n\n### Global reaction\nBy default, Hipbot reacts only to its HipChat mention.\nUse `global: true` option to match all messages:\n\n```ruby\non /^Hey I just met you$/, global: true do\n  reply('and this is crazy...')\nend\n```\n\n### Conditional reaction\nUse `:if` option to specify certain dynamic conditions:\n```ruby\non /^Is it friday\\?$/, if: -\u003e{ Time.now.friday? } do\n  reply('Yes, indeed')\nend\n```\n```ruby\nadmins = ['John Smith']\non /^add admin (.*)$/, if: -\u003e(sender){ admins.include?(sender.name) } do |user_name|\n  admins \u003c\u003c user_name\nend\n```\n```ruby\non /^choose volunteer$/, if: -\u003e(room){ room.users.count \u003e 3 } do\n  reply(\"Choosing #{room.users.sample}\")\nend\n```\n\n### Method reaction\nUse symbol instead of block to react with a instance method:\n```ruby\ndef hello(user_name)\n  reply(\"Hello #{user_name}!\")\nend\n\non /^My name is (.*)$/, :hello\n```\n\n### Presence reaction\nUse `on_presence` in the same way as `on` to make presence reactions:\n```ruby\nclass MyBot \u003c Hipbot::Bot\n  # ...\n  on_presence do |status|\n    case status\n    when 'unavailable'\n      reply(\"Bye bye, #{sender.name}!\")\n    when ''\n      reply(\"Welcome, #{sender.name}!\")\n    end\n  end\nend\n```\n\n### Scopes\nUse `scope` blocks to extract common options:\n```ruby\nconfigure do |c|\n  # ...\n  c.teams = { admins: ['John Edward', 'Mike Anderson'] }\nend\n\nscope from: :admins, room: true do\n  on /^restart server$/ do\n    # Restarting...\n  end\n\n  scope global: true do\n    on /^deploy production$/ do\n      # Deploying...\n    end\n\n    on /^check status$/ do\n      # Checking...\n    end\n  end\nend\n```\n\n### Default reactions\nDefault reaction can take the same options as regular one.\nHipbot fall backs to default reactions if there is no matching normal reaction.\n```ruby\ndefault do\n  reply(\"I don't understand you!\")\nend\n```\n```ruby\ndefault from: 'Mike Johnson' do\n  reply(\"Not you again, Mike!\")\nend\n```\n\n### Descriptions\nUse `desc` modifier to describe following reaction:\n```ruby\ndesc '@hipbot restart server_name - Restarts the server'\non /^restart (.*)$/ do |server|\n  if server.empty?\n    reply(\"Usage: #{reaction.desc}\")\n  else\n    # Restarting...\n  end\nend\n```\nYou can fetch the descriptions and create help reaction, eg:\n```ruby\non /^help$/ do\n  reply Hipbot.reactions.map(\u0026:desc).compact.join(\"\\n\")\nend\n```\n\n### User managment\nThis behavior is experimental and not officially supported by HipChat. Bot must be an admin in order to perform these actions.\n```ruby\non /^kick (.*)/ do |user_name|\n  user = Hipbot::User.find_by(name: user_name)\n  room.kick(user)\nend\n```\n```ruby\non /^invite (.*)$/ do |user_name|\n  user = Hipbot::User.find_by(name: user_name)\n  room.invite(user)\nend\n```\n\n### HTTP helpers\nUse `get`, `post`, `put` and `delete` helpers to preform a HTTP requests:\n```ruby\non /^curl (\\S+)$/ do |url|\n  get(url) do |response|\n    reply(response.code)\n    reply(response.headers)\n    reply(response.body)\n  end\nend\n```\n```ruby\non /^ping site/ do\n  get('http://example.com', ping: '1') # GET http://example.com?ping=1\nend\n```\n\n### Custom response helpers\nYou can define your own helpers and use them inside responses like this:\n```ruby\nmodule MyHipbotHelpers\n  def project_name\n    \"#{room.name}-project\"\n  end\nend\n\nclass Bot \u003c Hipbot::Bot\n  configure do |c|\n    # ...\n    c.helpers = MyHipbotHelpers\n  end\n\n  on /^what's the project name\\?$/ do\n    reply(project_name)\n  end\nend\n```\n\n### Plugins\nTo define a plugin, include `Hipbot::Plugin` module in your class:\n```ruby\nclass GreeterPlugin\n  include Hipbot::Plugin\n\n  on /^hello$/ do\n    reply('Hello there!')\n  end\nend\n```\n\nYou can access plugin data inside reaction with `plugin` helper:\n```ruby\nclass GreeterPlugin\n  include Hipbot::Plugin\n\n  attr_accessor :language\n\n  on /^hello$/ do\n    case plugin.language\n    when :en\n      reply(\"Hello!\")\n    when :pl\n      reply(\"Cześć!\")\n    when :jp\n      reply(\"おはよう！\")\n    end\n  end\nend\n\nGreeterPlugin.configure do |c|\n  c.language = :jp\nend\n```\nFor more examples, check out [hipbot-plugins](https://github.com/netguru/hipbot-plugins).\n\n### Exception handling\nDefine `on_exception` block in your Hipbot class to handle runtime exceptions:\n```ruby\nclass MyBot \u003c Hipbot::Bot\n  on_exception do |e|\n    hipbot_room = Hipbot::Room.find_by(name: 'hipbot room')\n    reply(e.message, hipbot_room)\n    # If exception was raised in reaction, there are some context variables available:\n    reply(\"#{e.message} raised by #{message.body} from #{sender} in #{room}\", hipbot_room)\n  end\nend\n```\n\n### Preloader for EventMachine\nIn order to use EventMachine runtime methods, define them within `on_preload` block in your Hipbot class:\n```ruby\nclass MyBot \u003c Hipbot::Bot\n  on_preload do\n    EM::add_periodic_timer(60) do\n      Updater::update_stock_prices\n      Updater::update_server_statuses\n    end\n  end\nend\n```\n\n### Storage\nHipbot uses in-memory hash storage by default, however you can use persistent\nstorage adapter to speed up boot time and extend the functionality.\n\n#### MongoDB\nIn order to use MongoDB storage, enable Mongoid adapter and add `allow_dynamic_fields: true` to your Mongoid config:\n```ruby\nrequire 'hipbot/storages/mongoid'\nconfigure do |c|\n  # ...\n  c.storage = Hipbot::Storages::Mongoid\nend\n```\nSample config file:\n```yaml\nsessions:\n  default:\n    hosts:\n      - localhost:27017\n    database: hipbot\noptions:\n  allow_dynamic_fields: true\n```\nYou can optionally override user and room classes with these base models:\n```ruby\nmodule Hipbot\n  class User\n    include Mongoid::Document\n\n    has_and_belongs_to_many :rooms, class_name: 'Hipbot::User', inverse_of: :users\n\n    field :email,      type: String\n    field :mention,    type: String\n    field :phone,      type: String\n    field :photo,      type: String\n    field :title,      type: String\n    field :is_online,  type: Boolean\n  end\nend\n```\n```ruby\nmodule Hipbot\n  class Room\n    include Mongoid::Document\n\n    has_and_belongs_to_many :users, class_name: 'Hipbot::User', inverse_of: :rooms\n\n    field :is_archived, type: Boolean\n    field :guest_url,   type: String\n    field :hipchat_id,  type: String\n    field :privacy,     type: String\n    field :topic,       type: String\n  end\nend\n```\n#### Other storage\nStorage adapter is included in room and user classes upon loading.\nMake sure your adapter implements all methods from [Hipbot::Storages::Base](https://github.com/pewniak747/hipbot/blob/master/lib/hipbot/storages/base.rb)\n```ruby\nmodule MyStorageAdapter\n  include Hipbot::Storages::Base\n  # ...\nend\n\nconfigure do |c|\n  # ...\n  c.storage = MyStorageAdapter\nend\n```\n\n## Contributing\n### To do:\n\n* add tests for Match class\n* add testing adapter for testing custom responses with RSpec\n* add HipChat API integration (?)\n\n### Done:\n* ~~add extended logging~~\n* ~~add plugins support~~\n* ~~rewrite SimpleMUCClient~~\n* ~~handle private messages callbacks~~\n* ~~handle auto joining on room invite~~\n* ~~add support for custom helpers~~\n  * ~~mentions - returns list of @mentions in message~~\n  * ~~sender_name - returns sender's first name~~\n  * ~~allow injecting custom module to response object, adding arbitrary methods~~\n* ~~handle reconnecting after disconnect/failure~~\n* ~~add support for multiple regexps for one response~~\n* ~~add support for responses in particular room (`on //, room: ['public'] do ...`)~~\n\nRead [the story behind creating HipBot](https://netguru.co/blog/posts/hipbot).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpewniak747%2Fhipbot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpewniak747%2Fhipbot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpewniak747%2Fhipbot/lists"}