WIADOMOŚCI

Slack, Bots, Coffeescript, DoD i SMS

Published on:29 / June / 2016
Jak w przypadku każdego zespołu  programistów pracujący w Scrum  OneAPI naszego zespołu ma jasne  kryterium ukończenia (ang. definition of  done, w skrócie DoD) dla każdego  zadania, tak aby ktoś inny mógł  wypróbować nową funkcję,  przeanalizować kod itp. Do tego stopnia,  że mamy nawet specjalną stronę na  Confluence.  
 
Pojęcie kogoś innego to najtrudniejsza  część tego procesu. Trzeba znaleźć tę  „szczęśliwą” osobę. I nagle wszyscy są  zajęci, śpieszą się albo głuchną.  Dlatego postanowiliśmy to  zrandomizować.  
 

PIERWSZA ITERACJA – SKRYPT  GROOVY  

... była dość trywialna:
 
def list = Arrays.asList(Member.values()) 
Collections.shuffle(list) 
print list.iterator().next() 

public enum Member { 
MATS, 
DENIS, 
PETAR, 
MATO 
}

…ale w parę sekund się skończyła.  Trzeba było zrandomizować i przydzielić  zadanie w JIRA. Problem polega na  tym, że łatwo to narazić. A nikt nie lubi  wykonywać zadań DoD. Nikt ci nie  zaufa.  
 

DRUGA ITERACJA –  SKRYPTOZAKŁADKA  

... była fajna, ale nadal nie rozwiązała  problemu:  
 
 javascript:(function () { 
function random(){ 
var devs = ["Matija Bruncic", "Denis Cutic", "Matija Matosevic"]; 
return devs[Math.floor(Math.random()*devs.length)] } var assignee = random(); 
document.querySelectorAll("[data-field-text='"+assignee+"']")[0].setAttribute("selected", "selected"); 
document.getElementById("assign-issue-submit").click() 
})();
 
Trzeba było przejść do tego zadania  JIRA w przeglądarce i uruchomić  skryptozakładkę. Nadal nikt ci nie zaufa.  A kod (tak samo jak w pierwszej iteracji)  musiał być inny dla każdego członka. I  pewnie nie działa w IE.  
 

TRZECIA ITERACJA –  SLACKBOT  

... była bardzo łatwa. I rozwiązała  problem uprzedzenia losowości.  
 
 
Slack ma domyślnego bota – Slackbot –  który może udzielić jednej z uprzednio  zdefiniowanych odpowiedzi na  uprzednio zdefiniowane żądania. Tyle  tylko, że człowiek się strasznie irytuje,  jeśli zostanie wybrany!  
 
 

OSTATNIA ITERACJA – HUBOT  

... jest najfajniejsza.  Jednym z powodów, dla których  wybraliśmy Slack, jest łatwość integracji  z botami i możliwość napisania fajnego i  przydatnego kodu. Postanowiliśmy  użyć Slack-Hubot. Pod maską kryje  się Hubot, jest łatwy w konfiguracji i  programowaniu. Nie będę się zagłębiać w  szczegóły, ale dokumentacja jest  dostępna w Internecie. 
 
 
I trzeba nadać mu imię. Nasz bot jest  twardzielem, więc nazwaliśmy go  Rambot. Logikę programuje się w  coffeescript. Przypomina to pisanie  aplikacji nodejs. Na przykład, można  zrobić listę interfejsów sieciowych:  
 
				 module.exports = (robot) -> 
robot.hear /rambot, where are you?/i, (res) -> 
os = require('os'); 
ifaces = os.networkInterfaces(); 
Object.keys(ifaces).forEach (ifname) -> 
ifaces[ifname].forEach (iface) -> 
if 'IPv4' != iface.family or iface.internal != false 
# skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses 
return 
# this interface has only one ipv4 adress 
res.send 'here somewhere: ' + ifname, iface.address 
return 
return


Wróćmy jednak do tematu! 

Stworzyliśmy prosty skrypt, który  rozpoznaje użytkownika wysyłającego  żądanie i losowo wybiera jednego z  pozostałych członków zespołu: 

						 users = ["denis.cutic", "josip", "mmatosevic", "mbruncic", "pducic"] 
module.exports = (robot) -> 
robot.hear /dod/i, (response) -> 
possible = (user for user in users when user != response.message.user.name) 
lucky = possible[Math.floor(Math.random() * possible.length)] 
response.send "Hi #{response.message.user.name}! Your dod is assigned to #{lucky}"

 

A potem nie mogliśmy przestać  programować, więc dodaliśmy funkcję,  która umożliwia Rambotowi aktualizację  osób, którym przydzielono zadanie  przez API JIRA:  
 
 jiraData = JSON.stringify({ 
fields: { 
assignee: { 
name: luckyJiraUsername 
} 
} 
}) 
response.http("https://jira.infobip.com/rest/api/2/issue/#{task}") 
.auth(jiraUsername, jiraPass) 
.header('Content-Type', 'application/json') 
.put(jiraData) (err, res, body) -> 
response.send(body)
 
Oraz oczywiście funkcję opartą na API  SMS firmy Infobip umożliwiajacą  powiadamianie szczęśliwca SMS-em: 
 
 ibData = JSON.stringify({ 
to:users[lucky].gsm, 
text:"You're the lucky one: #{task}" 
}) 
response.http("https://api.infobip.com/sms/1/text/single") 
.auth(infobipUsername, infobipPass) 
.header('Content-Type', 'application/json') 
.post(ibData) (err, res, body) -> 
response.send "sent SMS!"
 
Końcowy kod wygląda mniej więcej tak:
 
 jiraUsername = process.env.HUBOT_JIRA_USERNAME 
jiraPass = process.env.HUBOT_JIRA_PASS 
infobipUsername = process.env.HUBOT_IB_USERNAME 
infobipPass = process.env.HUBOT_IB_PASS 
users = { 
"denis.cutic": { 
jira: "dcutic", 
gsm: "3859********" 
}, 
josip: { 
jira: "jantolis", 
gsm: "3859********" 
}, 
mmatosevic: { 
jira: "mmatosevic", 
gsm: "3859********" 
}, 
mbruncic: { 
jira: "mbruncic", 
gsm: "3859********" 
}, 
pducic: { 
jira: "pducic", 
gsm: "3859********" }, 
} 
module.exports = (robot) -> 
robot.hear /dod (.*)/i, (response) -> 
task = response.match[1] 
possible = (user for user, value of users when user != response.message.user.name) 
lucky = possible[Math.floor(Math.random() * possible.length)] 
luckyJiraUsername = users[lucky].jira 
if jiraUsername? && jiraPass? 
jiraData = JSON.stringify({ 
fields: { 
assignee: { 
name: luckyJiraUsername 
} 
} 
}) 
response.http("https://jira.infobip.com/rest/api/2/issue/#{task}") 
.auth(jiraUsername, jiraPass) 
.header('Content-Type', 'application/json') 
.put(jiraData) (err, res, body) -> 
response.send(body) 
if infobipUsername? && infobipPass? 
ibData = JSON.stringify({ 
to:users[lucky].gsm, 
text:"You're the lucky one: #{task}" 
}) 
response.http("https://api.infobip.com/sms/1/text/single") 
.auth(infobipUsername, infobipPass) 
.header('Content-Type', 'application/json') 
.post(ibData) (err, res, body) -> 
response.send "sent SMS!" 
response.send "Hi #{response.message.user.name}! Your dod is assigned to #{lucky} "
 
Z perspektywy użytkownika wygląda to tak:
 
 
I to wszystko! Jeśli macie pomysły na  usprawnienia, akceptujemy żądania  aktualizacji. Cały kod jest dostępny  w GitHub.  
 
Przyjemności!  
 
Autor: Petar Ducic – inżynier  oprogramowania / lider zespołu