Use Cases
Sending System Messages in Channels (Webhooks/Groovy Process Action)
The description of this use case was removed from the Intrexx online help in September 2024 because Microsoft discontinued the corresponding functionality. For information on deadlines, please visit the following link: https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.*;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.*;
def id = g_record["96BA727080433D309562AE579A8AD507B8CDB8A2"].value
def title = g_record["1C07C0BA592B734D2BB2050F9A60C2D2132FDDE5"].value
def descr = g_record["364EB5C7725271398BA44FCBD74C1EF115F81A20"].value
def payload = """
{
"@type": "MessageCard","@context": "http://schema.org/extensions",
"themeColor": "0076D7",
"summary": "Larry Bryant created a new support ticket",
"sections": [{
"activityTitle": "${title}", "activitySubtitle": "${descr}",
"activityImage": "https://teamsnodesample.azurewebsites.net/static/img/image5.png",
"facts": [{
"name": "Assigned to",
"value": "Unassigned"
}, {
"name": "Status",
"value": "Not started"}],"markdown": true
}],
"potentialAction": [{
"@type": "ActionCard",
"name": "Add a comment",
"inputs": [{
"@type": "TextInput",
"id": "comment",
"isMultiline": false,
"title": "Add a comment here for this task"}],
"actions": [{
"@type": "HttpPOST",
"name": "Add comment",
"target": "https://docs.microsoft.com/outlook/actionable-messages"}]
}, {
"@type": "OpenUri",
"name": "Open in Intrexx","targets": [{
"os": "default",
"uri": "https://localhost:1337/path/app/?rq_AppGuid=5F0E4968468F8C4C36515228523D26B6CA16202F&rq_TargetPageGuid=6C02ABACBED2AFA937C1A6D97C2FA12937017C01&qs_link=946E2A263B44AB6052F8556F08B2EF64F49F7464&qs_STRID=${id}"
}]}, {
"@type": "ActionCard",
"name": "Change status",
"inputs": [{
"@type": "MultichoiceInput","id": "list",
"title": "Select a status",
"isMultiSelect": "false",
"choices": [{
"display": "In Progress",
"value": "1"}, {
"display": "Active","value": "2"
}, {"display": "Closed",
"value": "3"
}]
}],"actions": [{"@type": "HttpPOST",
"name": "Assign to me","target": "https://a75dafc0b855.ngrok.io/?qs_path=groovy/de/uplanet/lucy/sample/teams-change-ticket-status.groovy&id=${id}",
"body": "{\\"status\\":\\"{{list.value}}\\", \\"ticketID\\":\\"${id}\\", \\"ticketTitle\\":\\"${title}\\", \\"ticketDescr\\":\\"${descr}\\"}",
"bodyContentType": "application/json"
}]}]
}
"""
g_syslog.info(payload)
HttpClient client = HttpClient.newBuilder().build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create("<Webhook-URL>"))
.POST(BodyPublishers.ofString(payload))
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
g_syslog.info(response.statusCode());
g_syslog.info(response.body());
import java.io.*
import de.uplanet.io.IOHelper
ByteArrayOutputStream bos = new ByteArrayOutputStream();
IOHelper.copyStream(bos, g_request.getInputStream())
def id = g_request["id"]
def body = new String(bos.toByteArray())
def json = g_json.parse(body)
//def reply = "Hello from Intrexx, ${json.from.name}"
def title = json["ticketTitle"]
def descr = json["ticketDescr"]
def user = ""
if (g_session != null)
user = g_session.user.name
elseuser = "Supporter"
g_syslog.info("Ticket: " + id)
g_syslog.info(json)
def payload = """
{
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"themeColor": "0076D7",
"summary": "Larry Bryant created a new support ticket",
"sections": [{
"activityTitle": "${title}",
"activitySubtitle": "${descr}",
"activityImage": "https://teamsnodesample.azurewebsites.net/static/img/image5.png",
"facts": [{
"name": "Assigned to",
"value": "${user}"
}, {
"name": "Status",
"value": "Active"
}],
"markdown": true
}],
"potentialAction": [{
"@type": "OpenUri",
"name": "Open in Intrexx",
"targets": [{
"os": "default",
"uri": "https://localhost:1337/path/app/?rq_AppGuid=5F0E4968468F8C4C36515228523D26B6CA16202F&rq_TargetPageGuid=6C02ABACBED2AFA937C1A6D97C2FA12937017C01&qs_link=946E2A263B44AB6052F8556F08B2EF64F49F7464&qs_STRID=${id}"
}]
}]
}
"""
response.json()
response.setHeader("CARD-UPDATE-IN-BODY", "true")
write(payload)
Respond to Teams Messages in Intrexx
The description of this use case was removed from the Intrexx online help in September 2024 because Microsoft discontinued the corresponding functionality. For information on deadlines, please visit the following link: https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/
import java.io.*
import de.uplanet.io.IOHelper
import de.uplanet.tools.HMACVerifier
ByteArrayOutputStream bos = new ByteArrayOutputStream()
IOHelper.copyStream(bos, g_request.getInputStream())
def bytes = bos.toByteArray()
def req = g_request.getHttpServletRequest()
def authHeader = req.getHeader("Authorization")
g_syslog.info(authHeader)
if (authHeader == null || !authHeader.startsWith("HMAC")) {
g_syslog.error("Unauthorized request")
response.setStatus(401)
return
}
def key = "beuBhNzxssEKxljQ90tftG6Xc4Rf0gx/fjZR28V31aw="
def hmac = authHeader.substring(5)
g_syslog.info(hmac)
if (!HMACVerifier.verify(hmac, key, bytes)) {
g_syslog.error("Unauthorized request")
response.setStatus(401)
return
}
def body = new String(bytes)
def json = g_json.parse(body)
def reply = """
{
"type": "message",
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "**Open tickets for ${json.from.name}**"
},
{
"type": "TextBlock",
"text": "[Ticket 1](https://localhost:1337/)"
},
{
"type": "TextBlock",
"text": "[Ticket 2](https://localhost:1337/)"
}
]}
}
]
}
"""
g_syslog.info(reply)
response.json()
write(reply)
Interacting with bots in chats, group chats, and channels
Teams Messaging Extensions Action for Intrexx Share Tasks
Example
In Teams chats, group chats, or channels, a task should be created in Intrexx Share based on a new or existing message. In addition, a Teams Messaging Extension campaign featuring a bot is used. The Intrexx site provides an OData service for the task data group, which the bot calls to create tasks.
Teams Bot Deployment
Requirements
-
Microsoft Teams is installed, and you have an account
-
.NET Core SDK version 3.1
-
ngrok or an equivalent tunneling solution
-
Visual Studio or Visual Studio Code with the C#/.NET extension
-
Intrexx 21.09 Portal with Intrexx Share and the "Projects" module (this example is based on the Intrexx Demo Portal, but any other portal with Intrexx Share and the "Projects" module can be used)
Prepare and Run the Bot Web App
Step 1
Clone the repository using
git clone https://github.com/UnitedPlanet/ms365-integration-samples.git
or download it here: teams-messaging-extensions-action-for-ix-tasks.zip
Step 2
If you're using Visual Studio:
-
Start Visual Studio
-
Select the "File / Open / Project/Solution" menu
-
Navigate to the "teams-messaging-extensions-action" folder
-
Select the "TeamsMessagingExtensionsAction.csproj" file here
Step 3
Run `ngrok - point to port 3978` or use a reverse proxy to tunnel external HTTPS requests to the Intrexx portal server
ngrok http -host-header=rewrite 3978
Step 4
Create a Bot Framework Registration Resource in Azure.
-
Use the current HTTPS URL that you received when you ran ngrok. Be sure to include the "/api/messages" path used in this example: https://xxxxx.ngrok.io/api/messages
-
Make sure the Microsoft Teams channel is enabled
-
If you don't have an Azure account, you can use this Bot Framework registration.
Step 5
Update the bot's `appsettings.json` configuration to use the Microsoft App ID and App Password from the Bot Framework registration. Please note that the app password corresponds to the "client secret" in the Azure portal, and you can generate a new client secret at any time. Also, provide the Intrexx username and password for authentication with the Intrexx OData service.
Step 6
This step applies specifically to Microsoft Teams.
-
In the "manifest.json" file, located in the "teamsAppManifest" folder, replace the placeholder string "<<YOUR-MICROSOFT-APP-ID>>". Depending on the scenario, the Microsoft App ID may appear multiple times in the "manifest.json" file.
-
Compress the contents of the teamsAppManifest folder to create a "manifest.zip" file.
-
Upload the manifest.zip file to Teams. To do this, click "Upload a custom app" in the app view.
Step 7
Run your bot—either by pressing F5 in Visual Studio or by using `dotnet run` in the appropriate folder.
Edit Projects and Recipient List for the Task Card
The Tasks card contains two lists of values for projects and recipients. The values can be edited in the file "teams-messaging-extensions-action/Resources/taskCard.json".
{
"type": "TextBlock",
"text": "Project",
"size": "default"
},
{
"type": "Input.ChoiceSet",
"id": "taskProject",
"style": "compact",
"isMultiSelect": false,
"value": "",
"choices": [
{
"title": "My Tasks",
"value": "FEF3C9FB5C9F6F45C25E7EFD6149BB39F44CF04C"
},
{
"title": "Trade fair at the conference center",
"value": "C2838ACA9DBCB25EF5447D0C847FC9BFD53DEE81"
},
{
"title": "The digital kitchen",
"value": "2C44139F68ECC98CBB604CD1866FAF866E2449A6"
}
]
},
{
"type": "TextBlock",
"text": "Assign to",
"size": "default"
},
{
"type": "Input.ChoiceSet",
"id": "taskResponsibleUserId",
"style": "compact",
"isMultiSelect": false,
"value": "",
"choices": [
{
"title": "Administrator",
"value": "1"
},
{
"title": "PeterM",
"value": "6"
},
{
"title": "TinaM",
"value": "16"
},
{
"title": "FrankN",
"value": "17"
}
]
}
The project list contains the project name and GUID. These values can be found in the "TSK_CATEGORIES" database table. The values in the recipient list are Intrexx usernames and their user IDs. These values can be found in the "VBLUSER" view.
Deploy a bot in Azure
Under " Deploying Your Bot," you'll find a complete list of deployment instructions.
Deploy the Intrexx OData Service
To make the OData service available in your portal, unzip the following ZIP file into the portal directory "internal/cfg/odata/producer" and restart the portal service. The OData service configuration should then be available in the "Integration" module in Portal Manager.
Download msteams-msg-extensions-share-task-odata.zip
Test Task Generation
In the Teams channel, click "New Conversation" and then click the app icon. Then select "Create Intrexx Task." The Tasks card appears when the bot is registered correctly and the bot service for Microsoft Teams is available via the ngrok tunnel.
After the assignment is submitted, the bot responds with a summary and a link to the assignment in Intrexx.
The task can be viewed immediately in Intrexx.
Authorized users will receive a notification in Intrexx Share.
The task is also displayed in the overviews and the task table.
Using the MS Graph SDK in a Groovy process action
Authentication
The following classes are available in Groovy scripts for authentication using the MS Graph API.
import de.uplanet.lucy.server.odata.v4.consumer.http.MsGraphSdkAuthenticationProviderFactory
def clientId = "msgraphsdk" // Name of the MS Graph OAuth2 configuration
def authFactory = new MsGraphSdkAuthenticationProviderFactory()
def accessTokenProvider = authFactory.createForCurrentUser(clientId) // 1) Anmeldung mit aktuellem Portaluser (Authorization Code)
def accessTokenProvider = authFactory.createForImpersonatedUser(clientId, "<USER_GUID>") // 2) Anmeldung mit statischem Benutzer aus Konfiguration (Password)
def accessTokenProvider = authFactory.createForServiceUser(clientId, "<USER_GUID>") // 3) Anmeldung mit Service Account aus Konfiguration (Client Credentials)
-
Login with a current portal user (Authorization Code Flow)
This sign-in type redirects the portal user in the browser to sign in to Microsoft 365 and then back to Intrexx. Suitable only for interactive activities.
-
Login with a static user from the configuration (Password Flow)
With this sign-in type, a Microsoft 365 user from the configuration is used.
-
Sign-in with a Service Account from Configuration (Client Credentials Flow)
With this sign-in type, a Microsoft 365 service account from the configuration is used.
Graph SDK Examples
Sending an Email in Processes
The following example shows how an email can be sent from processes or page handlers.
import de.uplanet.lucy.server.odata.v4.consumer.http.MsGraphSdkAuthenticationProviderFactory
import java.util.ArrayList;
import okhttp3.Request;
import com.microsoft.graph.models.BodyType;
import com.microsoft.graph.models.ChatMessage;
import com.microsoft.graph.models.EmailAddress;
import com.microsoft.graph.models.Importance;
import com.microsoft.graph.models.ItemBody;
import com.microsoft.graph.models.Message;
import com.microsoft.graph.models.Recipient;
import com.microsoft.graph.requests.GraphServiceClient;
def clientId = "msgraphsdk" // Name of the MS Graph OAuth2 configuration
def authFactory = new MsGraphSdkAuthenticationProviderFactory()
def accessTokenProvider = authFactory.createForImpersonatedUser(clientId, "7312F993D0DA4CECCA9AE5A9D865BE142DE413EA") // 2) Anmeldung mit statischem Benutzer aus Konfiguration (Password)
def graphClient =
GraphServiceClient
.builder()
.authenticationProvider(accessTokenProvider)
.buildClient();
def message = new Message();
message.subject = "Hello";
message.importance = Importance.LOW;
def body = new ItemBody();
body.contentType = BodyType.HTML;
body.content = "Hi from <b>Intrexx</b>!";
message.body = body;
def toRecipientsList = new ArrayList<Recipient>();
def toRecipients = new Recipient();
def emailAddress = new EmailAddress();
emailAddress.address = "john.smith@intrexx365.com";
toRecipients.emailAddress = emailAddress;
toRecipientsList.add(toRecipients);
message.toRecipients = toRecipientsList;
def msgResponse = graphClient.me().messages()
.buildRequest()
.post(message);
def msgId = msgResponse.id;
graphClient.me().messages(msgId)
.send()
.buildRequest()
.post();
For more information on this topic, click here:
https://docs.microsoft.com/en-us/graph/api/user-post-messages
View and Change the Current User's Status
The following example shows how to retrieve and modify the presence information for the current portal user. You can download the application here: ms365presence.zip
Groovy Page Rendering Handler (Before) for displaying the presence on a page (via VTL):
import de.uplanet.lucy.server.odata.v4.consumer.http.MsGraphSdkAuthenticationProviderFactory
import com.microsoft.graph.requests.GraphServiceClient
def clientId = "teams" // Name of the MS Graph OAuth2 configuration
def authFactory = new MsGraphSdkAuthenticationProviderFactory()
def accessTokenProvider = authFactory.createForCurrentUser(clientId)
def graphClient = GraphServiceClient
.builder().authenticationProvider(accessTokenProvider)
.buildClient()
def presence = graphClient.me().presence()
.buildRequest()
.get()
g_log.info(presence.activity)
g_log.info(presence.availability)
g_sharedState["my_activity"] = presence.activity
g_sharedState["my_availability"] = presence.availability
You can find more information here:
https://docs.microsoft.com/en-us/graph/api/presence-get?view=graph-rest-beta&tabs=java
Groovy Page Action Handler (Before) for changing the presence via a view page:
import de.uplanet.lucy.server.odata.v4.consumer.http.MsGraphSdkAuthenticationProviderFactory
import com.microsoft.graph.requests.GraphServiceClient
import com.microsoft.graph.models.PresenceSetPresenceParameterSet;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
def clientId = "teams" // Name of the MS Graph OAuth2 configuration
def authFactory = new MsGraphSdkAuthenticationProviderFactory()
def accessTokenProvider = authFactory.createForServiceUser(clientId)
def graphClient = GraphServiceClient
.builder()
.authenticationProvider(accessTokenProvider)
.buildClient()
def upn = g_session.user?.emailBiz // the current user's upn
def appId = "89094413-170f-440b-a3e7-6f8e45257531" // the azure app id
def myId = graphClient.users(upn).buildRequest().get().id; // get the current user's Azure id
def availability = g_request["dropdowncontrolAA8B5A6C"] // get the status
if (availability == null)
availability = "Available"
def acitivity = availability == "Busy" ? "InACall" : availability; // get the activity
def expirationDuration = DatatypeFactory.newInstance().newDuration("PT5M"); // define expiration
def params = PresenceSetPresenceParameterSet.newBuilder() // build parameter set
.withActivity(acitivity)
.withSessionId(appId).withAvailability(availability)
.withExpirationDuration(expirationDuration)
.build();
graphClient.users(myId).presence().setPresence(params).buildRequest().post();
g_log.info("Set presence for user ${upn}");
You can find more information here:
https://docs.microsoft.com/en-us/graph/api/presence-setpresence?view=graph-rest-beta&tabs=java
More Information
Process Action - Create Team/Channel
Process Action - Teams Message Action






