Start working and refactoring

This commit is contained in:
Alexander Meindl
2017-05-08 12:11:08 +02:00
parent 715ac9f753
commit ca69cfd67f
11 changed files with 427 additions and 392 deletions

34
.rubocop.yml Normal file
View File

@@ -0,0 +1,34 @@
Rails:
Enabled: true
AllCops:
TargetRubyVersion: 2.1
Metrics/LineLength:
Max: 140
# Offense count: 34
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 60
# Offense count: 11
Metrics/CyclomaticComplexity:
Max: 13
# Offense count: 10
Metrics/PerceivedComplexity:
Max: 14
# Offense count: 15
Metrics/AbcSize:
Max: 25
Style/Documentation:
Enabled: false
Rails/HttpPositionalArguments:
Enabled: false
Metrics/BlockLength:
Max: 50

View File

@@ -1,3 +1 @@
source 'https://rubygems.org'
gem 'httpclient'

View File

@@ -2,6 +2,7 @@ Released under the MIT license.
Project redmine-slack is Copyright (c) 2014-2016 by Samuel Cormier-Iijima (https://github.com/sciyoshi/redmine-slack).
Fork project redmine_mattermost is Copyright (c) 2015-2016 by AltSol (https://github.com/altsol/redmine_mattermost).
Fork project redmine_rocketchat is Copyright (c) 20176 by Phlegx (https://github.com/phlegx/redmine_rocketchat).
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -1,9 +1,9 @@
# Rocket.Chat plugin for Redmine
# Messenger plugin for Redmine
This plugin posts updates to issues in your Redmine installation to a Rocket.Chat
channel.
This plugin posts updates to issues in your Redmine installation to Slack, Rocket.Chat or Mattermost channel.
Redmine Supported versions: 3.3.x.
Redmine Supported versions: 2.0.x - 3.3.x.
## Screenshot

56
init.rb
View File

@@ -1,38 +1,28 @@
require 'redmine'
require 'redmine_messenger'
require_dependency 'redmine_rocketchat/listener'
Redmine::Plugin.register :redmine_messenger do
name 'Redmine Messenger'
author 'AlphaNodes GmbH'
url 'https://github.com/alphanodes/redmine_messenger'
author_url 'https://alphanodes.com/'
description 'Messenger integration for Slack, Rocketchat and Mattermost support'
version '0.6.2-dev'
Redmine::Plugin.register :redmine_rocketchat do
name 'Redmine Rocket.Chat'
author 'Egon Zemmer'
url 'https://github.com/phlegx/redmine_rocketchat'
author_url 'https://phlegx.com'
description 'Rocket.Chat integration'
version '0.6.1'
requires_redmine version_or_higher: '3.3.0'
requires_redmine :version_or_higher => '2.0.0'
settings \
:default => {
'callback_url' => 'https://rocket.chat/hooks/my_rocket_chat_token',
'channel' => 'redmine',
'icon' => 'https://raw.githubusercontent.com/phlegx/redmine_rocketchat/assets/icon.png',
'username' => 'redmine.bot',
'display_watchers' => '0',
'auto_mentions' => '1',
'post_updates' => '1',
'new_include_description' => '1',
'updated_include_description' => '1',
'post_private_issues' => '1',
'post_private_notes' => '1',
'post_wiki_updates' => '0'
},
:partial => 'settings/rocketchat_settings'
end
ActionDispatch::Callbacks.to_prepare do
require_dependency 'issue'
unless Issue.included_modules.include? RedmineRocketchat::IssuePatch
Issue.send(:include, RedmineRocketchat::IssuePatch)
end
settings default: {
callback_url: 'https://rocket.chat/hooks/my_rocket_chat_token',
channel: 'redmine',
icon: 'https://raw.githubusercontent.com/phlegx/redmine_rocketchat/assets/icon.png',
username: 'redmine.bot',
display_watchers: '0',
auto_mentions: '1',
post_updates: '1',
new_include_description: '1',
updated_include_description: '1',
post_private_issues: '1',
post_private_notes: '1',
post_wiki_updates: '0'
}, partial: 'settings/messenger_settings'
end

9
lib/redmine_messenger.rb Normal file
View File

@@ -0,0 +1,9 @@
# Redmine Messenger plugin for Redmine
Rails.configuration.to_prepare do
# Patches
require_dependency 'redmine_messenger/patches/issue_patch'
# Hooks
require_dependency 'redmine_messenger/hooks'
end

View File

@@ -0,0 +1,322 @@
# Redmine Messenger plugin for Redmine
require 'httpclient'
module RedmineMessenger
class MessengerListener < Redmine::Hook::Listener
def redmine_rocketchat_issues_new_after_save(context={})
issue = context[:issue]
channels = channels_for_project issue.project
url = url_for_project issue.project
post_private_issues = post_private_issues_for_project(issue.project)
return unless channels.present? && url
return if issue.is_private? && post_private_issues != '1'
msg = "[#{escape issue.project}] #{escape issue.author} created <#{object_url issue}|#{escape issue}>#{mentions issue.description if Setting.plugin_redmine_rocketchat[:auto_mentions] == '1'}"
attachment = {}
attachment[:text] = escape issue.description if issue.description && Setting.plugin_redmine_rocketchat[:new_include_description] == '1'
attachment[:fields] = [{
title: I18n.t(:field_status),
value: escape(issue.status.to_s),
short: true
}, {
title: I18n.t(:field_priority),
value: escape(issue.priority.to_s),
short: true
}, {
title: I18n.t(:field_assigned_to),
value: escape(issue.assigned_to.to_s),
short: true
}]
attachment[:fields] << {
title: I18n.t(:field_watcher),
value: escape(issue.watcher_users.join(', ')),
short: true
} if Setting.plugin_redmine_rocketchat[:display_watchers] == '1'
speak msg, channels, attachment, url
end
def redmine_rocketchat_issues_edit_after_save(context={})
issue = context[:issue]
journal = context[:journal]
channels = channels_for_project issue.project
url = url_for_project issue.project
post_private_issues = post_private_issues_for_project(issue.project)
post_private_notes = post_private_notes_for_project(issue.project)
return unless channels.present? and url and Setting.plugin_redmine_rocketchat[:post_updates] == '1'
return if issue.is_private? and post_private_issues != '1'
return if journal.private_notes? and post_private_notes != '1'
msg = "[#{escape issue.project}] #{escape journal.user.to_s} updated <#{object_url issue}|#{escape issue}>#{mentions journal.notes if Setting.plugin_redmine_rocketchat[:auto_mentions] == '1'}"
attachment = {}
attachment[:text] = escape journal.notes if journal.notes && Setting.plugin_redmine_rocketchat[:updated_include_description] == '1'
attachment[:fields] = journal.details.map { |d| detail_to_field d }
speak msg, channels, attachment, url
end
def model_changeset_scan_commit_for_issue_ids_pre_issue_update(context={})
issue = context[:issue]
journal = issue.current_journal
changeset = context[:changeset]
channels = channels_for_project issue.project
url = url_for_project issue.project
post_private_issues = post_private_issues_for_project(issue.project)
return unless channels.present? and url and issue.save
return if issue.is_private? and post_private_issues != '1'
msg = "[#{escape issue.project}] #{escape journal.user.to_s} updated <#{object_url issue}|#{escape issue}>"
repository = changeset.repository
if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
host, port, prefix = $2, $4, $5
revision_url = Rails.application.routes.url_for(
:controller => 'repositories',
:action => 'revision',
:id => repository.project,
:repository_id => repository.identifier_param,
:rev => changeset.revision,
:host => host,
:protocol => Setting.protocol,
:port => port,
:script_name => prefix
)
else
revision_url = Rails.application.routes.url_for(
:controller => 'repositories',
:action => 'revision',
:id => repository.project,
:repository_id => repository.identifier_param,
:rev => changeset.revision,
:host => Setting.host_name,
:protocol => Setting.protocol
)
end
attachment = {}
attachment[:text] = ll(Setting.default_language, :text_status_changed_by_changeset, "<#{revision_url}|#{escape changeset.comments}>")
attachment[:fields] = journal.details.map { |d| detail_to_field d }
speak msg, channels, attachment, url
end
def controller_wiki_edit_after_save(context = { })
return unless Setting.plugin_redmine_rocketchat[:post_wiki_updates] == '1'
project = context[:project]
page = context[:page]
user = page.content.author
project_url = "<#{object_url project}|#{escape project}>"
page_url = "<#{object_url page}|#{page.title}>"
comment = "[#{project_url}] #{page_url} updated by *#{user}*"
channels = channels_for_project project
url = url_for_project project
return unless channels.present? and url
attachment = nil
unless page.content.comments.empty?
attachment = {}
attachment[:text] = "#{escape page.content.comments}"
end
speak comment, channels, attachment, url
end
def speak(msg, channels, attachment=nil, url=nil)
return if channels.blank?
url = Setting.plugin_redmine_rocketchat[:rocketchat_url] unless url
username = Setting.plugin_redmine_rocketchat[:username]
icon = Setting.plugin_redmine_rocketchat[:icon]
params = {
:text => msg,
:link_names => 1,
}
params[:username] = username if username
params[:attachments] = [attachment] if attachment
if icon and not icon.empty?
if icon.start_with? ':'
params[:icon_emoji] = icon
else
params[:icon_url] = icon
end
end
channels.each do |channel|
params[:channel] = channel
begin
client = HTTPClient.new
client.ssl_config.cert_store.set_default_paths
client.ssl_config.ssl_version = :auto
client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
client.post_async url, {:payload => params.to_json}
rescue Exception => e
Rails.logger.warn("cannot connect to #{url}")
Rails.logger.warn(e)
end
end
end
private
def escape(msg)
msg.to_s.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;')
end
def object_url(obj)
if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
host, port, prefix = $2, $4, $5
Rails.application.routes.url_for(obj.event_url({:host => host, :protocol => Setting.protocol, :port => port, :script_name => prefix}))
else
Rails.application.routes.url_for(obj.event_url({:host => Setting.host_name, :protocol => Setting.protocol}))
end
end
def url_for_project(proj)
return nil if proj.blank?
cf = ProjectCustomField.find_by_name('Rocket.Chat URL')
[
(proj.custom_value_for(cf).value rescue nil),
(url_for_project proj.parent),
Setting.plugin_redmine_rocketchat[:rocketchat_url],
].flatten.find(&:present?)
end
def post_private_issues_for_project(proj)
return nil if proj.blank?
cf = ProjectCustomField.find_by_name('Rocket.Chat Post private issues')
[
(proj.custom_value_for(cf).value rescue nil),
(post_private_issues_for_project proj.parent),
Setting.plugin_redmine_rocketchat[:post_private_issues],
].flatten.find(&:present?)
end
def post_private_notes_for_project(proj)
return nil if proj.blank?
cf = ProjectCustomField.find_by_name('Rocket.Chat Post private notes')
[
(proj.custom_value_for(cf).value rescue nil),
(post_private_notes_for_project proj.parent),
Setting.plugin_redmine_rocketchat[:post_private_notes],
].flatten.find(&:present?)
end
def channels_for_project(proj)
return nil if proj.blank?
cf = ProjectCustomField.find_by_name('Rocket.Chat Channel')
val = [
(proj.custom_value_for(cf).value rescue nil),
(channels_for_project proj.parent),
Setting.plugin_redmine_rocketchat[:channel],
].flatten.find(&:present?)
# Channel name '-' or empty '' is reserved for NOT notifying
return [] if val.nil? or val.to_s == ''
return [] if val.to_s == '-'
return val.split(",") if val.is_a? String
val
end
def detail_to_field(detail)
field_format = nil
if detail.property == "cf"
key = CustomField.find(detail.prop_key).name rescue nil
title = key
field_format = CustomField.find(detail.prop_key).field_format rescue nil
elsif detail.property == 'attachment'
key = 'attachment'
title = I18n.t :label_attachment
else
key = detail.prop_key.to_s.sub('_id', '')
title = I18n.t "field_#{key}"
end
short = true
value = escape detail.value.to_s
case key
when 'title', 'subject', 'description'
short = false
when 'tracker'
tracker = Tracker.find(detail.value) rescue nil
value = escape tracker.to_s
when 'project'
project = Project.find(detail.value) rescue nil
value = escape project.to_s
when 'status'
status = IssueStatus.find(detail.value) rescue nil
value = escape status.to_s
when 'priority'
priority = IssuePriority.find(detail.value) rescue nil
value = escape priority.to_s
when 'category'
category = IssueCategory.find(detail.value) rescue nil
value = escape category.to_s
when 'assigned_to'
user = User.find(detail.value) rescue nil
value = escape user.to_s
when 'fixed_version'
version = Version.find(detail.value) rescue nil
value = escape version.to_s
when 'attachment'
attachment = Attachment.find(detail.prop_key) rescue nil
value = "<#{object_url attachment}|#{escape attachment.filename}>" if attachment
when 'parent'
issue = Issue.find(detail.value) rescue nil
value = "<#{object_url issue}|#{escape issue}>" if issue
end
case field_format
when 'version'
version = Version.find(detail.value) rescue nil
value = escape version.to_s
end
value = '-' if value.empty?
result = { title: title, value: value }
result[:short] = true if short
result
end
def mentions(text)
return nil if text.nil?
names = extract_usernames text
names.present? ? '\nTo: ' + names.join(', ') : nil
end
def extract_usernames(text = '')
text = '' if text.nil?
# rocketchat usernames may only contain lowercase letters, numbers,
# dashes, dots and underscores and must start with a letter or number.
text.scan(/@[a-z0-9][a-z0-9_\-.]*/).uniq
end
end
end

View File

@@ -0,0 +1,34 @@
# Redmine Messenger plugin for Redmine
module RedmineMessenger
module Patches
module IssuePatch
def self.included(base) # :nodoc:
base.send(:include, InstanceMethods)
base.class_eval do
after_create :create_from_issue
after_save :save_from_issue
end
end
module InstanceMethods
def create_from_issue
@create_already_fired = true
Redmine::Hook.call_hook(:redmine_rocketchat_issues_new_after_save, issue: self)
true
end
def save_from_issue
unless @create_already_fired
Redmine::Hook.call_hook(:redmine_rocketchat_issues_edit_after_save, issue: self, journal: self.current_journal) unless self.current_journal.nil?
end
true
end
end
end
end
end
unless Issue.included_modules.include? RedmineMessenger::Patches::IssuePatch
Issue.send(:include, RedmineMessenger::Patches::IssuePatch)
end

View File

@@ -1,33 +0,0 @@
module RedmineRocketchat
module IssuePatch
def self.included(base) # :nodoc:
base.extend(ClassMethods)
base.send(:include, InstanceMethods)
base.class_eval do
unloadable # Send unloadable so it will not be unloaded in development
after_create :create_from_issue
after_save :save_from_issue
end
end
module ClassMethods
end
module InstanceMethods
def create_from_issue
@create_already_fired = true
Redmine::Hook.call_hook(:redmine_rocketchat_issues_new_after_save, { :issue => self})
return true
end
def save_from_issue
unless @create_already_fired
Redmine::Hook.call_hook(:redmine_rocketchat_issues_edit_after_save, {:issue => self, :journal => self.current_journal}) unless self.current_journal.nil?
end
return true
end
end
end
end

View File

@@ -1,320 +0,0 @@
require 'httpclient'
class RocketchatListener < Redmine::Hook::Listener
def redmine_rocketchat_issues_new_after_save(context={})
issue = context[:issue]
channels = channels_for_project issue.project
url = url_for_project issue.project
post_private_issues = post_private_issues_for_project(issue.project)
return unless channels.present? and url
return if issue.is_private? and post_private_issues != '1'
msg = "[#{escape issue.project}] #{escape issue.author} created <#{object_url issue}|#{escape issue}>#{mentions issue.description if Setting.plugin_redmine_rocketchat[:auto_mentions] == '1'}"
attachment = {}
attachment[:text] = escape issue.description if issue.description and Setting.plugin_redmine_rocketchat[:new_include_description] == '1'
attachment[:fields] = [{
:title => I18n.t(:field_status),
:value => escape(issue.status.to_s),
:short => true
}, {
:title => I18n.t(:field_priority),
:value => escape(issue.priority.to_s),
:short => true
}, {
:title => I18n.t(:field_assigned_to),
:value => escape(issue.assigned_to.to_s),
:short => true
}]
attachment[:fields] << {
:title => I18n.t(:field_watcher),
:value => escape(issue.watcher_users.join(', ')),
:short => true
} if Setting.plugin_redmine_rocketchat[:display_watchers] == '1'
speak msg, channels, attachment, url
end
def redmine_rocketchat_issues_edit_after_save(context={})
issue = context[:issue]
journal = context[:journal]
channels = channels_for_project issue.project
url = url_for_project issue.project
post_private_issues = post_private_issues_for_project(issue.project)
post_private_notes = post_private_notes_for_project(issue.project)
return unless channels.present? and url and Setting.plugin_redmine_rocketchat[:post_updates] == '1'
return if issue.is_private? and post_private_issues != '1'
return if journal.private_notes? and post_private_notes != '1'
msg = "[#{escape issue.project}] #{escape journal.user.to_s} updated <#{object_url issue}|#{escape issue}>#{mentions journal.notes if Setting.plugin_redmine_rocketchat[:auto_mentions] == '1'}"
attachment = {}
attachment[:text] = escape journal.notes if journal.notes and Setting.plugin_redmine_rocketchat[:updated_include_description] == '1'
attachment[:fields] = journal.details.map { |d| detail_to_field d }
speak msg, channels, attachment, url
end
def model_changeset_scan_commit_for_issue_ids_pre_issue_update(context={})
issue = context[:issue]
journal = issue.current_journal
changeset = context[:changeset]
channels = channels_for_project issue.project
url = url_for_project issue.project
post_private_issues = post_private_issues_for_project(issue.project)
return unless channels.present? and url and issue.save
return if issue.is_private? and post_private_issues != '1'
msg = "[#{escape issue.project}] #{escape journal.user.to_s} updated <#{object_url issue}|#{escape issue}>"
repository = changeset.repository
if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
host, port, prefix = $2, $4, $5
revision_url = Rails.application.routes.url_for(
:controller => 'repositories',
:action => 'revision',
:id => repository.project,
:repository_id => repository.identifier_param,
:rev => changeset.revision,
:host => host,
:protocol => Setting.protocol,
:port => port,
:script_name => prefix
)
else
revision_url = Rails.application.routes.url_for(
:controller => 'repositories',
:action => 'revision',
:id => repository.project,
:repository_id => repository.identifier_param,
:rev => changeset.revision,
:host => Setting.host_name,
:protocol => Setting.protocol
)
end
attachment = {}
attachment[:text] = ll(Setting.default_language, :text_status_changed_by_changeset, "<#{revision_url}|#{escape changeset.comments}>")
attachment[:fields] = journal.details.map { |d| detail_to_field d }
speak msg, channels, attachment, url
end
def controller_wiki_edit_after_save(context = { })
return unless Setting.plugin_redmine_rocketchat[:post_wiki_updates] == '1'
project = context[:project]
page = context[:page]
user = page.content.author
project_url = "<#{object_url project}|#{escape project}>"
page_url = "<#{object_url page}|#{page.title}>"
comment = "[#{project_url}] #{page_url} updated by *#{user}*"
channels = channels_for_project project
url = url_for_project project
return unless channels.present? and url
attachment = nil
unless page.content.comments.empty?
attachment = {}
attachment[:text] = "#{escape page.content.comments}"
end
speak comment, channels, attachment, url
end
def speak(msg, channels, attachment=nil, url=nil)
return if channels.blank?
url = Setting.plugin_redmine_rocketchat[:rocketchat_url] unless url
username = Setting.plugin_redmine_rocketchat[:username]
icon = Setting.plugin_redmine_rocketchat[:icon]
params = {
:text => msg,
:link_names => 1,
}
params[:username] = username if username
params[:attachments] = [attachment] if attachment
if icon and not icon.empty?
if icon.start_with? ':'
params[:icon_emoji] = icon
else
params[:icon_url] = icon
end
end
channels.each do |channel|
params[:channel] = channel
begin
client = HTTPClient.new
client.ssl_config.cert_store.set_default_paths
client.ssl_config.ssl_version = :auto
client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
client.post_async url, {:payload => params.to_json}
rescue Exception => e
Rails.logger.warn("cannot connect to #{url}")
Rails.logger.warn(e)
end
end
end
private
def escape(msg)
msg.to_s.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;')
end
def object_url(obj)
if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
host, port, prefix = $2, $4, $5
Rails.application.routes.url_for(obj.event_url({:host => host, :protocol => Setting.protocol, :port => port, :script_name => prefix}))
else
Rails.application.routes.url_for(obj.event_url({:host => Setting.host_name, :protocol => Setting.protocol}))
end
end
def url_for_project(proj)
return nil if proj.blank?
cf = ProjectCustomField.find_by_name('Rocket.Chat URL')
[
(proj.custom_value_for(cf).value rescue nil),
(url_for_project proj.parent),
Setting.plugin_redmine_rocketchat[:rocketchat_url],
].flatten.find(&:present?)
end
def post_private_issues_for_project(proj)
return nil if proj.blank?
cf = ProjectCustomField.find_by_name('Rocket.Chat Post private issues')
[
(proj.custom_value_for(cf).value rescue nil),
(post_private_issues_for_project proj.parent),
Setting.plugin_redmine_rocketchat[:post_private_issues],
].flatten.find(&:present?)
end
def post_private_notes_for_project(proj)
return nil if proj.blank?
cf = ProjectCustomField.find_by_name('Rocket.Chat Post private notes')
[
(proj.custom_value_for(cf).value rescue nil),
(post_private_notes_for_project proj.parent),
Setting.plugin_redmine_rocketchat[:post_private_notes],
].flatten.find(&:present?)
end
def channels_for_project(proj)
return nil if proj.blank?
cf = ProjectCustomField.find_by_name('Rocket.Chat Channel')
val = [
(proj.custom_value_for(cf).value rescue nil),
(channels_for_project proj.parent),
Setting.plugin_redmine_rocketchat[:channel],
].flatten.find(&:present?)
# Channel name '-' or empty '' is reserved for NOT notifying
return [] if val.nil? or val.to_s == ''
return [] if val.to_s == '-'
return val.split(",") if val.is_a? String
val
end
def detail_to_field(detail)
field_format = nil
if detail.property == "cf"
key = CustomField.find(detail.prop_key).name rescue nil
title = key
field_format = CustomField.find(detail.prop_key).field_format rescue nil
elsif detail.property == 'attachment'
key = 'attachment'
title = I18n.t :label_attachment
else
key = detail.prop_key.to_s.sub('_id', '')
title = I18n.t "field_#{key}"
end
short = true
value = escape detail.value.to_s
case key
when 'title', 'subject', 'description'
short = false
when 'tracker'
tracker = Tracker.find(detail.value) rescue nil
value = escape tracker.to_s
when 'project'
project = Project.find(detail.value) rescue nil
value = escape project.to_s
when 'status'
status = IssueStatus.find(detail.value) rescue nil
value = escape status.to_s
when 'priority'
priority = IssuePriority.find(detail.value) rescue nil
value = escape priority.to_s
when 'category'
category = IssueCategory.find(detail.value) rescue nil
value = escape category.to_s
when 'assigned_to'
user = User.find(detail.value) rescue nil
value = escape user.to_s
when 'fixed_version'
version = Version.find(detail.value) rescue nil
value = escape version.to_s
when 'attachment'
attachment = Attachment.find(detail.prop_key) rescue nil
value = "<#{object_url attachment}|#{escape attachment.filename}>" if attachment
when 'parent'
issue = Issue.find(detail.value) rescue nil
value = "<#{object_url issue}|#{escape issue}>" if issue
end
case field_format
when 'version'
version = Version.find(detail.value) rescue nil
value = escape version.to_s
end
value = '-' if value.empty?
result = { :title => title, :value => value }
result[:short] = true if short
result
end
def mentions text
return nil if text.nil?
names = extract_usernames text
names.present? ? '\nTo: ' + names.join(', ') : nil
end
def extract_usernames text = ''
if text.nil?
text = ''
end
# rocketchat usernames may only contain lowercase letters, numbers,
# dashes, dots and underscores and must start with a letter or number.
text.scan(/@[a-z0-9][a-z0-9_\-.]*/).uniq
end
end