故事背景:项目想要使用Jira进行售后问题管理,但是公司网络环境并不是很好,只能内网服务器操作,所以我想着内网服务器写一个Python程序处理这个事情。我想达成的目的就是想要Jira上某人给另一个人分配了一个问题,然后会用邮件通知的方式通知。。但是公司邮箱。。。一言难尽,所以只能使用钉钉的API了,所以我要做的就俩事,一个是接收JSON并且解析,第二个事就是发送给钉钉的API。
为什么用Python呢,因为相对来说实现起来简单一点,所以就用的Python。。就很随心所欲。。
简单说一下逻辑和想法,首先要用Python的flask库搭建一个简单的接受POST请求的服务器,也就是一个接收Jira的网络钩子发出的JSON的一个小小服务器。然后要对于过来的JSON进行解析,拿到我们想要的信息之后,再进行发送。所以,第一步得先吧JSON处理一下。
*def appHandleJSON(toHandlesJSON):
logger.debug(str(toHandlesJSON.items()))
if toHandlesJSON is not None:
if toHandlesJSON['issue_event_type_name'] == "issue_assigned":
selectFromMysql(str(toHandlesJSON['changelog']['items'][0]['toString']))
sendPost(toHandlesJSON['changelog']['items'][0]['fromString'], toHandlesJSON['issue']['key'])
logger.debug("fromString" + str(toHandlesJSON['changelog']['items'][0]['fromString']))
logger.debug("toString" + str(toHandlesJSON['changelog']['items'][0]['toString']))
logger.info("Send post and handle Json all ok!")
if toHandlesJSON['webhookEvent'] == "comment_created":
logger.info("comment_created, do nothing!")*
稍微解释一下.....
logger.debug(str(toHandlesJSON.items()))
这个是为了保存一下必要的LOG,.items()这个方法源于字典对象,实际上是要以列表返回可遍历的(键, 值) 元组数组,保存一下基础信息if toHandlesJSON is not None:
为了让来了的JSON数据不是空的,所以简单判断一下if toHandlesJSON['issue_event_type_name'] == “issue_assigned”:
这个是为了判断JIRA的事件类型,是不是有人给派送任务了selectFromMysql
这个函数的定义如下,为了从数据库中找到想找的人的电话号,因为钉钉需要用注册电话号来实现@功能,具体详情可以看官方文档
*def selectFromMysql(selectObject):
conn = pymysql.Connect(
host='localhost', # 你的数据库地址
port=3306, # 端口号
user='jiradb', # 用户名
passwd='*********', # 密码
db='nameAndnumber', # 数据库名
charset='utf8')
cursor = conn.cursor()
sql = "select num from numbers where sName ='" + selectObject + "';"
cursor.execute(sql)
global sqlResult
sqlResult = cursor.fetchall()
cursor.close()
conn.close()*
没错,我用了个麻烦的方法,还利用了服务器里面的MYSQL,最简单的利用哈哈哈哈,前面就是简单的建立连接,有一些必要的地方被我用**代替了。
cursor = conn.cursor()
创建链接数据库的对象sql = “select num from numbers where sName ='” + selectObject + “';”
简单的查询语句被,要提一句的是,数据库的字段有三个,一个主键自增ID,一个姓名sName,一个电话号sNumber,selectObject 就是要查询的对象,fetchall就是用来提取数据的方法,然后别忘了close
既然都说到这,我就顺便写一下send post请求的函数我是咋想的
def sendPost(fromstring, toHandleJsonDate_key):
logger.info("Start sendPost!")
if sqlResult is not None: //判断SQL查询结果是否不是空的
logger.info(sqlResult)
url = 'https://oapi.dingtalk.com/robot/send?access_token=******' //钉钉机器人的接口地址,具体看最后面图
headers = {'Content-Type': 'application/json;charset=utf-8'} //http的post请求头
data = { //http的请求数据
"msgtype": "text",
"text": {
"content": "[jira] 您收到了一条来自 " + str(
fromstring) + " 的任务,请注意登录 [jira] 进行处理,任务链接为http://*************" +
str(toHandleJsonDate_key)
},
"at": { //@的实现,atMobiles要写被AT人的手机号
"atMobiles": [
sqlResult[0][0]
],
"isAtAll": "false" //是否@全体成员,默认现在就是false
}
}
proxies = {'http': "http://username:password@ip:port/", //网络环境一言难尽啊,只能用代理拯救一下了
'https': "http://username:password@ip:port/"}
try: //发送POST请求,超时了会打印log的(我没见过)
r = requests.post(url, data=json.dumps(data), headers=headers, proxies=proxies, timeout=2)
logger.info(r.status_code)
logger.info(r.json())
logger.info("sendPost is ok")
logger.debug("sqlresult is " + str(sqlResult[0][0]))
except ReadTimeout:
logger.warning('Timeout')
else:
logger.warning("sqlResult is none!")
其实就剩下一点没说过了,,就是如何接收,接收很简单,就是做一个app route,然后他自己就收了
*# webhook event server
@app.route("/webhook/event", methods=['POST'])
def event():
json_data = json.loads(str(request.data, encoding='utf-8'))
to_handle_json_thread = Thread(target=appHandleJSON(json_data))
to_handle_json_thread.start()
#to_handle_json_thread.join()
return "SendPost and Handle Json all OK!"*
还有初始化的处理,log的初始化,log文件的初始化
# log output setting and init
logger = logging.getLogger("LOGGING")
logging.basicConfig(filename='/root/log/Handle.log',
format='%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(message)s',
level=logging.INFO)
logger.info("log init is ok")
完整代码如下:(代码写的很菜,别喷俺...)
import json, requests, pymysql, logging
from threading import Thread
from time import sleep, ctime
from flask import Flask, request
from logging import handlers
from logging.handlers import RotatingFileHandler
from requests.exceptions import ReadTimeout
app = Flask(__name__)
global sqlResult
# log output setting and init
logger = logging.getLogger("LOGGING")
logging.basicConfig(filename='/root/log/Handle.log',
format='%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(message)s',
level=logging.INFO)
logger.info("log init is ok")
# webhook event server
@app.route("/webhook/event", methods=['POST'])
def event():
json_data = json.loads(str(request.data, encoding='utf-8'))
to_handle_json_thread = Thread(target=appHandleJSON(json_data))
to_handle_json_thread.start()
#to_handle_json_thread.join()
return "SendPost and Handle Json all OK!"
# send post to DingTalk webhook
def sendPost(fromstring, toHandleJsonDate_key):
logger.info("Start sendPost!")
if sqlResult is not None:
logger.info(sqlResult)
url = 'https://oapi.dingtalk.com/robot/send?access_token=*******************'
headers = {'Content-Type': 'application/json;charset=utf-8'}
data = {
"msgtype": "text",
"text": {
"content": "[jira] 您收到了一条来自 " + str(
fromstring) + " 的任务,请注意登录 [jira] 进行处理,任务链接为http://********" +
str(toHandleJsonDate_key)
},
"at": {
"atMobiles": [
sqlResult[0][0]
],
"isAtAll": "false"
}
}
proxies = {'http': "http://username:password@ip:port/",
'https': "http://username:password@ip:port/"}
try:
r = requests.post(url, data=json.dumps(data), headers=headers, proxies=proxies, timeout=2)
logger.info(r.status_code)
logger.info(r.json())
logger.info("sendPost is ok")
logger.debug("sqlresult is " + str(sqlResult[0][0]))
except ReadTimeout:
logger.warning('Timeout')
else:
logger.warning("sqlResult is none!")
# Get phone numbers from mysql
def selectFromMysql(selectObject):
conn = pymysql.Connect(
host='localhost', # 你的数据库地址
port=3306, # 端口号
user='jiradb', # 用户名
passwd='*********', # 密码
db='nameAndnumber', # 数据库名
charset='utf8')
cursor = conn.cursor()
sql = "select num from numbers where sName ='" + selectObject + "';"
cursor.execute(sql)
global sqlResult
sqlResult = cursor.fetchall()
cursor.close()
conn.close()
# thread class really to do
def appHandleJSON(toHandlesJSON):
logger.debug(str(toHandlesJSON.items()))
if toHandlesJSON is not None:
if toHandlesJSON['issue_event_type_name'] == "issue_assigned":
selectFromMysql(str(toHandlesJSON['changelog']['items'][0]['toString']))
sendPost(toHandlesJSON['changelog']['items'][0]['fromString'], toHandlesJSON['issue']['key'])
logger.debug("fromString" + str(toHandlesJSON['changelog']['items'][0]['fromString']))
logger.debug("toString" + str(toHandlesJSON['changelog']['items'][0]['toString']))
logger.info("Send post and handle Json all ok!")
if toHandlesJSON['webhookEvent'] == "comment_created":
logger.info("comment_created, do nothing!")
def main():
app.run(host="0.0.0.0", port=65500)
logger.info("The app is run!")
if __name__ == "__main__":
main()
学到了flask的简单应用,字典的数据处理,Python的全局变量如何应用,以及线程的创建等等,具体基础知识整理的话,我往后再整理一些吧。。
附件1:钉钉机器人配置(其实有弄好的,但是如果有自己的需求就自定义,毕竟我这网络环境限制,,莫得办法...)
这个要配合代码,我代码里有一个[JIRA],这个就是关键字
Token就有了,放到应该放的位置