Commit 915f4b4f authored by Frank Bergmann's avatar Frank Bergmann

Initial Import

parents
Pipeline #986 failed with stages
<?xml version="1.0"?>
<!-- Generated by the OpenACS Package Manager -->
<package key="intranet-slack" url="http://openacs.org/repository/apm/packages/intranet-slack" type="apm_application">
<package-name>]project-open[ Slack Integration</package-name>
<pretty-plural>]project-open[ Slack Integration</pretty-plural>
<initial-install-p>f</initial-install-p>
<singleton-p>t</singleton-p>
<implements-subsite-p>f</implements-subsite-p>
<inherit-templates-p>f</inherit-templates-p>
<auto-mount>intranet-slack</auto-mount>
<version name="0.1d" url="http://openacs.org/repository/download/apm/intranet-slack-0.1d.apm">
<owner url="mailto:frank.bergmann@project-open.com">Frank Bergmann</owner>
<summary>Slack Integration</summary>
<vendor url="http://www.project-open.com/">]project-open[</vendor>
<description format="text/plain">Slack Integration</description>
<maturity>0</maturity>
<provides url="intranet-slack" version="0.1d"/>
<callbacks>
</callbacks>
<parameters>
<!-- No version parameters -->
</parameters>
</version>
</package>
# /packages/intranet-slack/tcl/intranet-slack-procs.tcl
#
# Copyright (c) 2003-2016 ]project-open[
#
# All rights reserved. Please check
# http://www.project-open.com/license/ for details.
ad_library {
@author frank.bergmann@project-open.com
}
# ----------------------------------------------------------------------
# Entry point for command execution
# ----------------------------------------------------------------------
ad_proc -public im_slack_action {
-line
{ -var_list ""}
} {
Main entry into Slack integration.
Is called by the HTTP page receiving any Slack
incoming call.
} {
set line [string tolower $line]
set var_list [string tolower $var_list]
ns_log Notice "im_slack_action: line=$line"
ns_log Notice "im_slack_action: vars=$var_list"
array set command_hash [im_slack_commands]; # Get the list of all available commands
set command [lindex $line 0]; # Line taken as a list of strings, returns first element
set rest_of_line [lrange $line 1 end]
if {[info exists command_hash($command)]} {
set command_info_list $command_hash($command)
ns_log Notice "im_slack_action: command=$command, rest_of_line=$rest_of_line, command_info_hash=$command_info_list]"
set arg_list [im_slack_arg_parser -line $rest_of_line -command_info_list $command_info_list]
ad_return_complaint 1 $arg_list
ns_log Notice "im_slack_action: cmd=$cmd"
if {[catch $cmd err_msg]} {
set err_msg [ad_print_stack_trace]
ns_log Notice "im_slack_action: Error executing cmd=$cmd: $err_msg"
set err_lines ""
foreach err_line [split $err_msg "\n"] {
lappend err_lines "{\"text\": \"$err_line\"}"
}
return "{\n\"text\": \"Error executing command '$command':\",\n\"attachments\": \[\n[join $err_lines ",\n"]\n\]}"
} else {
ns_log Notice "im_slack_action: Successfully executed command=$cmd"
return "{\n\"text\": \"Done.\",\n\"attachments\": \[\]}"
}
} else {
set available_commands_json [im_slack_available_commands_json]
return "{\n\"text\": \"Command '$command' does not exist. Options:\",\n\"attachments\": \[\n$available_commands_json\n\]}"
}
return "Not implemented yet: line='$line'"
}
# ----------------------------------------------------------------------
# Parser
# Pulls a list of arguments from the messenger line
# ----------------------------------------------------------------------
ad_proc -public im_slack_arg_parser {
-line:required
-command_info_list:required
} {
Main parser. We get the meta info about the identified
command plus the rest of the line without the command.
Returns a TCL command line for calling the proc's TCL
implementation plus an error message
} {
ns_log Notice "im_slack_arg_parser: line='$line', command_info=$command_info_list"
array set command_info $command_info_list
# ad_return_complaint 1 "<pre>im_slack_arg_parser:\n line='$line',\n command_info=[array get command_info]</pre>"
# Initialize the list of found args
set arg_list [list]
set err_msg ""
foreach argname [array names command_info] {
if {"proc" eq $argname} { continue }
if {"help" eq $argname} { continue }
if {"name" eq $argname} { continue }
set arg_spec_list $command_info($argname)
array unset arg_spec_hash
array set arg_spec_hash $arg_spec_list
ns_log Notice "im_slack_arg_parser: line='$line', argname=$argname, arg_spec_list=$arg_spec_list"
# Check if we can find the object
set tuple [im_slack_arg_parser_one_arg -arg $argname -line $line -arg_spec_list $arg_spec_list]
set object [lindex $tuple 0]
if {"" ne $object} {
ns_log Notice "im_slack_arg_parser: Found arg='$argname' + object='$object' in line='$line'"
set line [lindex $tuple 1];# This is the line without the object returned by the parser_arg_type
lappend arg_list $argname
lappend arg_list $object
continue
} else {
ns_log Notice "im_slack_arg_parser: Did not find arg='$argname' + object='$object' in line='$line'"
set required_p $arg_spec_hash(required)
if {$required_p} {
# We didn't find a required argument - return an error message and abort
set err_msg "Didn't find required argument='$argname' in line='$line'"
return [list {} $err_msg]
}
}
# We didn't find a "direct" match with the argname.
# Now check the default section
# ... ToDo
}
return [list $arg_list $err_msg]
}
ad_proc -public im_slack_arg_parser_one_arg {
-line:required
-arg:required
-arg_spec_list:required
} {
Tries to extract an argument from an input line.
Returns a list with two entries:
<ul>
<li>The result - a non-empty string if something was found.
<li>The rest of the list with the found arg removed
</ul>
} {
ns_log Notice "im_slack_arg_parser_arg_type: arg=$arg, line='$line', arg_spec_list=$arg_spec_list"
array set arg_spec_hash $arg_spec_list
set required $arg_spec_hash(required)
set type $arg_spec_hash(type)
set default $arg_spec_hash(default)
set arg_list [list]
# Check if <arg_name> is part of the line
set poss [lsearch -all $line $arg]
foreach pos $poss {
# Now search behind the position for a type specified in the command_info
ns_log Notice "im_slack_arg_parser: Found arg=$arg in pos=$pos in line='$line'"
set tuple [im_slack_arg_parser_arg_type -line $line -pos [expr $pos+1] -type $type -arg_spec_list $arg_spec_list]
set object [lindex $tuple 0]
if {"" ne $object} {
ns_log Notice "im_slack_arg_parser: Found arg='$arg' + object='$object' at pos=$pos in line='$line'"
set line [lindex $tuple 1];# This is the line without the object returned by the parser_arg_type
set line [lreplace $line $pos $pos]; # Now we also remove the arg
lappend arg_list $arg
lappend arg_list $object
ns_log Notice "im_slack_arg_parser: After removing arg='$arg' the line is: line='$line'"
break
}
}
return [list $object $line]
}
ad_proc -public im_slack_arg_parser_arg_type {
-line:required
-pos:required
-type: required
-arg_spec_list:required
} {
Tries to extract an argument of type=type from line at the given pos.
Returns a list with two element: An object and the rest of the string.
} {
set object [lindex $line $pos]
set rest_of_line $line
if {$pos < [llength $line]} {
set rest_of_line [lreplace $line $pos $pos]
}
return [list $object $rest_of_line]
}
# ----------------------------------------------------------------------
# Implementations
# ----------------------------------------------------------------------
ad_proc -public im_slack_log_hours {
{ -who "" }
{ -when "" }
{ -how_many "" }
{ -on "" }
} {
Logs hours for the current user on the current project.
} {
return ""
}
# ----------------------------------------------------------------------
# Meta-Information
# ----------------------------------------------------------------------
ad_proc -public im_slack_commands {} {
Returns a data-structure with the list of available
commands, plus there meta-data.
Each command has a slack short name, followed by a
list that defines the TCL procedure handling the
command and a list of arguments for that command.
Each of these arguments consists of two strings:
<ul>
<li>A preposition ("what", "on", "for", ...)
<li>An argument type ("number", "project", ...)
</ul>
The following commands are currently defined
<ul>
<li>log - log hours
<li>alert - request alerts for certain projects
</ul>
} {
set command_info {
help {
name help
proc im_slack_help
help "Shows help"
}
log {
name log
proc im_slack_log_hours
help "Log a number of hours on a project.\nExample: log 10 hours on #2016_0001"
who { name who type person required 1 default {user_id} }
how_many { name how_many type number required 1 }
on { name on type project required 0 default {channel_project} }
}
alert {
name alert
proc im_slack_create_alert
help "Request alerts from a given project.\nExample: alert on #2016_0001"
on { name on type project required 1 default {channel_project} }
}
}
# Remove whitespaces in list above...
#regsub -all {\s+} $command_info " " result
return $command_info
}
ad_proc -public im_slack_available_commands_json {} {
Create a JSON list with the list of available commands,
suitable to be used as "attachment" in a Slack reply
} {
array set command_hash [im_slack_commands]
set results [list]
foreach command [lsort [array names command_hash]] {
array unset command_info
array set command_info $command_hash($command)
lappend results "{\"text\": \"$command - $command_info(help)\"}"
}
return [join $results ",\n"]
}
# ----------------------------------------------------------------------
# PackageID
# ----------------------------------------------------------------------
ad_proc -public im_package_slack_id {} {
Returns the package id of the intranet-slack module
} {
return [util_memoize im_package_slack_id_helper]
}
ad_proc -private im_package_slack_id_helper {} {
return [db_string im_package_core_id {
select package_id from apm_packages
where package_key = 'intranet-slack'
} -default 0]
}
# ----------------------------------------------------------------------
# Test Cases
# ----------------------------------------------------------------------
ad_proc -public im_slack_test {} {
Checks some standard test cases
} {
set test_cases {
{ command "log" line "/log 8 hours on #2016_0002" };# log to specified project
{ command "log" line "/log 8 hours" };# Assumes local channel
}
}
{'success': true, 'message': 'success', 'data': {'object_id': <%= [im_new_object_id] %>}}
ad_page_contract {
Slack integration page
} {
{ channel_id "" }
{ channel_name "" }
{ command "" }
{ response_url "" }
{ team_domain "" }
{ team_id "" }
{ text "" }
{ token "" }
{ user_id "" }
{ user_name "" }
}
set header_vars [ns_conn headers]
set url [ns_conn url]
set current_user_id [ad_conn user_id]
set current_user_name [db_string uname "select im_name_from_user_id(:current_user_id)" -default "unknown"]
set client_ip [ns_set get $header_vars "Client-ip"]
set referer_url [ns_set get $header_vars "Referer"]
set peer_ip [ns_conn peeraddr]
set current_url [im_url_with_query]
array set vars {}
set vars(channel_id) $channel_id
set vars(channel_name) $channel_name
set vars(command) $command
set vars(response_url) $response_url
set vars(team_domain) $team_domain
set vars(team_id) $team_id
set vars(text) $text
set vars(token) $token
set vars(user_id) $user_id
set vars(user_name) $user_name
set vars(current_url) $current_url
set vars(peer_ip) $peer_ip
set vars(user_id) $user_id
foreach var [ad_ns_set_keys $header_vars] {
set value [ns_set get $header_vars $var]
# set vars($var) $value
}
# Ignore the generic /po command, but keep other commands
# as the first word in the line.
# So "/po log 10 hours" is equivalent to "/log 10 hours".
if {"/po" eq $command} {
set line $text
} else {
set line "[string range $command 1 end] $text"
}
# Start the actual action
ns_log Notice "im_slack: vars=[array get vars]"
set result [im_slack_action -line $line -var_list [array get vars]]
ns_log Notice "im_slack: result=$result"
if {[string range "{}" 0 0] eq [string range $result 0 0]} {
ns_log Notice "im_slack: Found a JSON string return value - returning directly"
ns_return 200 "application/json" $result
} else {
ns_log Notice "im_slack: Found a text string return value - packaging as JSON"
# set attachments [list]
set response_type "in_channel"; # Response visible to all channel members
set response_type "ephemeral"; # Only visible to the user who issued the command
ns_return 200 "application/json" "{\n\"response_type\": \"$response_type\",\n\"text\": \"$result\"\n}"
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment