Skip to content
This repository was archived by the owner on Jul 14, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
.DS_Store
18 changes: 17 additions & 1 deletion config/locales/client.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ en:
tags: "If specified, this rule will only apply to topics which have at least one of these tags"

provider:

#######################################
########### SLACK STRINGS #############
#######################################
Expand Down Expand Up @@ -230,6 +229,23 @@ en:
help: "The URL provided when you create a new incoming webhook"
errors:
invalid_channel: "That channel does not exist on Microsoft Teams"
#####################################################
######### MICROSOFT POWER AUTOMATE STRINGS ##########
#####################################################
powerautomate:
title: "Microsoft Power Automate"
param:
name:
title: "Name"
help: "A name for the channel (only shown in the Discourse admin interface)"
webhook_url:
title: "Webhook URL"
help: "The URL provided when you create a new incoming webhook"
errors:
invalid_webhook: "That webhook URL is not valid."
########################################
######### CISCO WEBEX STRINGS ##########
########################################
webex:
title: "Webex Teams"
param:
Expand Down
6 changes: 6 additions & 0 deletions config/locales/server.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ en:
chat_integration_teams_enabled: "Enable the Microsoft Teams chat integration provider"
chat_integration_teams_excerpt_length: "Microsoft Team post excerpt length"

####################################################
######## MICROSOFT POWER AUTOMATE SETTINGS #########
####################################################
chat_integration_powerautomate_enabled: "Enable the Microsoft Power Automate integration provider"
chat_integration_powerautomate_excerpt_length: "Microsoft Power Automate post excerpt length"

###########################################
######## WEBEX TEAMS SETTINGS #########
###########################################
Expand Down
13 changes: 10 additions & 3 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,16 @@ chat_integration:
default: false
chat_integration_teams_excerpt_length:
default: 400
###########################################
######## WEBEX TEAMS SETTINGS #########
###########################################
####################################################
######## MICROSOFT POWER AUTOMATE SETTINGS #########
####################################################
chat_integration_powerautomate_enabled:
default: false
chat_integration_powerautomate_excerpt_length:
default: 400
#################################
######## WEBEX SETTINGS #########
#################################
chat_integration_webex_enabled:
default: false
chat_integration_webex_excerpt_length:
Expand Down
1 change: 1 addition & 0 deletions lib/discourse_chat_integration/provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def self.mount_engines
require_relative "provider/flowdock/flowdock_provider"
require_relative "provider/groupme/groupme_provider"
require_relative "provider/teams/teams_provider"
require_relative "provider/powerautomate/powerautomate_provider"
require_relative "provider/webex/webex_provider"
require_relative "provider/google/google_provider"
require_relative "provider/guilded/guilded_provider"
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# frozen_string_literal: true

module DiscourseChatIntegration::Provider::PowerAutomateProvider
PROVIDER_NAME = "powerautomate"
PROVIDER_ENABLED_SETTING = :chat_integration_powerautomate_enabled
CHANNEL_PARAMETERS = [
{ key: "name", regex: '^\S+$', unique: true },
{ key: "webhook_url", regex: '^https:\/\/\S+$', unique: true, hidden: true },
]

def self.trigger_notification(post, channel, rule)
message = get_message(post)
uri = URI(channel.data["webhook_url"])

http = FinalDestination::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == "https")

req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
req.body = message.to_json
response = http.request(req)

unless response.kind_of? Net::HTTPSuccess
if response.body.include?("Invalid webhook URL")
error_key = "chat_integration.provider.powerautomate.errors.invalid_webhook"
else
error_key = nil
end
raise ::DiscourseChatIntegration::ProviderError.new info: {
error_key: error_key,
request: req.body,
response_code: response.code,
response_body: response.body,
}
end
end

def self.get_message(post)
display_name = "@#{post.user.username}"
full_name =
if SiteSetting.enable_names && post.user.name.present?
post.user.name
else
""
end

topic = post.topic

category = ""
if topic.category&.uncategorized?
category = "[#{I18n.t("uncategorized_category_name")}]"
elsif topic.category
category =
(
if (topic.category.parent_category)
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
else
"[#{topic.category.name}]"
end
)
end

message = {
type: "message",
attachments: [
{
contentType: "application/vnd.microsoft.card.adaptive",
contentUrl: nil,
content: {
type: "AdaptiveCard",
body: [
{
type: "TextBlock",
size: "Large",
weight: "Bolder",
text:
"[#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}](#{post.full_url})",
wrap: true,
spacing: "None",
},
{
type: "ColumnSet",
columns: [
{
type: "Column",
items: [
{
type: "Image",
style: "Person",
url: post.user.small_avatar_url,
altText: full_name,
size: " Small",
},
],
width: "auto",
},
{
type: "Column",
items: [
{ type: "TextBlock", weight: "Bolder", text: full_name, wrap: true },
{
type: "TextBlock",
spacing: "None",
text: display_name,
isSubtle: true,
wrap: true,
},
],
width: "stretch",
},
],
},
{
type: "TextBlock",
text:
post.excerpt(
SiteSetting.chat_integration_powerautomate_excerpt_length,
text_entities: true,
strip_links: true,
remap_emoji: true,
),
wrap: true,
},
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
version: "1.2",
},
},
],
}

message
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe DiscourseChatIntegration::Provider::PowerAutomateProvider do
let(:post) { Fabricate(:post) }

describe ".trigger_notifications" do
before { SiteSetting.chat_integration_powerautomate_enabled = true }

let(:chan1) do
DiscourseChatIntegration::Channel.create!(
provider: "powerautomate",
data: {
name: "discourse",
webhook_url:
"https://prod-189.westus.logic.azure.com:443/workflows/c94b462906e64fe8a7299043706be96e/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=-cmkg1oG-88dP3Yqdh62yTG1LUtJFcB91rQisorfw_w",
},
)
end

it "sends a webhook request" do
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(body: "1")
described_class.trigger_notification(post, chan1, nil)
expect(stub1).to have_been_requested.once
end

it "handles errors correctly" do
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(status: 400, body: "{}")
expect(stub1).to have_been_requested.times(0)
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
::DiscourseChatIntegration::ProviderError,
)
expect(stub1).to have_been_requested.once
end
end
end