简要记录开发中遇到的事情
事件起因
最近测试提了一个问题:虽然每次开发发布了测试版本的时候都要在钉钉群里面通知,但是也会经常忘记了,这样就会导致耽误测试及修改bug的后续整个进度。
一直知道钉钉里面有个webhook功能,同时看到fir.im里面可以设置webhook,基于此,便可以实现上传版本后自动通知到钉钉群里面。
解决办法
在钉钉里面的机器人 可以在GitHub等平台配置webhook后进行 push消息提示,所以每次push都会在钉钉里面提醒。如此,若在fir.im里面加入hook地址应该也会在上传apk后收到消息提示,但是打开fir.im后并没有看到钉钉平台的支持,

但是找到了一个自定义webhook设置,于是就进行了自定义的配置,但在钉钉上一直没有收到推送消息,无奈只好联系客服以帮助查找问题,虽然客服反应较慢但最后还是给了回复

要指向自己的服务器,这个太麻烦了,这么一个小功能没有必要专门开服务(这一点还是希望fir.im可以早点对一些大平台支持)。于是只好另想办法,查找fir.im的文档发现可以接口上传安装包,那就只好迂回路线——接口上传安装包,成功后调用钉钉给出webhook地址发送推送消息。其实后面就很简单了,主要就几个接口调用而已。
最近有看python,所以打算用python来显现一下。首先用python做了一个简单的界面,包括有选择安装包以及上传按钮等

python很不熟练,可能很多东西都使用不当,比如没有异步请求接口等,花了较久的时间写代码,最终达到了自己的目的。在发送到钉钉的提醒消息里面有下载地址,我这里是在代码里面配置死了的。推送钉钉的消息样式可以参考open-doc.dingtalk.com/docs/doc.ht…定制。以下是代码
# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox
import tkinter.filedialog
import requests
import os
import re
import subprocess
import zipfile
import json
root = Tk()
root.title('APK上传fir.im')
root.geometry('600x400')
path = StringVar()
pathShow = StringVar() # 显示文件名
#默认的一个apitoken
apiTokenDefult = "你的默认apitoken"
packagename = ""
versionCode = 0
versionName = ""
name = ""
saveIconName = ""
mfmUrl = "XXX"
mfmPwd = "XXX"
fgcUrl = "XXX"
fgcPwd = "XXX"
fgjUrl = "XXX"
fgjPwd = "XXX"
#这里是钉钉消息需要@的人的手机号
atList = ["12345","12345"]
#这里是webhook地址
notifyUrl = "https://oapi.dingtalk.com/robot/send?access_token=XXX"
def appInfo():
#根据自己的aapt地址修改
output = subprocess.Popen('D:/android_sdk/build-tools/21.0.0/aapt dump badging ' + path.get(),
shell=False,stdout=subprocess.PIPE).stdout.readlines()
for line in output:
if "package: name" in line.decode():
match = re.compile("package: name='(\S+)' versionCode='(\d+)' versionName='(\S+)'").match(line.decode())
global packagename
packagename = match.group(1)
global versionCode
versionCode = match.group(2)
global versionName
versionName = match.group(3)
if 'application: label' in line.decode():
matchName = re.compile("application: label='(\S+)'").match(line.decode())
global name
name = matchName.group(1)
if 'application-icon-320:' in line.decode():
icon = re.compile("application-icon-320:'(\S+)'").match(line.decode())
iconPath = icon.group(1)
#保存到本地
zz = zipfile.ZipFile( path.get() )
iconData = zz.read(iconPath)
global saveIconName
#临时保存icon的位置
saveIconName = "D:/python3.6/icon.png";
with open(saveIconName,'w+b') as saveIconFile:
saveIconFile.write(iconData)
def selectPath():
path_ = tkinter.filedialog.askopenfilename(filetypes=[("apk文件", ".apk")])
path.set(path_)
temp = path.get().split('/')
pathShow.set(temp[len(temp)-1])
appInfo()
def doSubmit():
if not packagename or not versionName or not name or versionCode==0:
messagebox.showinfo('提示','获取apk信息失败')
return
res = requests.post("http://api.fir.im/apps",data={"type":"android","bundle_id":packagename,
"api_token":apiToken.get() or apiTokenDefult})
if res.status_code != 201:
messagebox.showinfo('提示','请求返回错误')
return
data = res.json()
icon = data['cert']['icon']
apk = data['cert']['binary']
#上传icon
uploadIcon = requests.post(icon['upload_url'],data={"key":icon['key'],'token':icon['token']},
files={'file':('icon.png',open(saveIconName,'rb'))})
if uploadIcon.status_code!=200:
messagebox.showinfo('提示',"上传ICON成功")
return
#上传文件
upload = requests.post(apk['upload_url'],data={"key":apk['key'],'token':apk['token'],
'x:name':name,'x:version':versionName,'x:build':versionCode,
'x:changelog':des.get()},
files={'file':(pathShow.get(),open(path.get(),'rb'))})
if upload.status_code==200 and upload.json()["is_completed"]==True:
messagebox.showinfo('提示',"上传成功")
#通知推送消息
downUrl = ""
downPwd = ""
if "maifangma" in packagename:
downUrl = mfmUrl
downPwd = mfmPwd
elif "fangguancha" in packagename:
downUrl = fgcUrl
downPwd = fgcPwd
elif "fangguanjia" in packagename:
downUrl = fgjUrl
downPwd = fgjPwd
else:
messagebox.showinfo('提示',"下载地址无效")
listat = ""
for at in atList:
listat+="@"+at
data = {"msgtype": "markdown",
"markdown":{"title":name+"新版本",
"text":"### "+name+"Android新版本\n"+"> 版本:"+versionName+"\n\n> 更新描述:"+
des.get()+"\n\n> [点击下载("+ ("无效链接" if not downPwd else ("密码:"+downPwd)) +")]("+downUrl+")\n\n > "+listat},
"at":{"atMobiles":atList}}
notify = requests.post(notifyUrl,data=json.dumps(data),
headers={"Content-Type":"application/json;charset=utf-8"})
else:
messagebox.showinfo('提示',"上传失败:"+upload.status_code)
Label(root,text='api_token:',width=20).grid(row=0,column=0)
apiToken = Entry(root)
apiToken.grid(row=0,column=1)
Label(root,text='文件路径:').grid(row=1,column=0)
Button(root, text = "选择文件", command = selectPath).grid(row = 1, column = 1)
Label(root, textvariable = pathShow,bg='white').grid(row = 2, column = 1,columnspan = 2)
Label(root,text='更新描述:').grid(row=3,column=0)
des = Entry(root)
des.grid(row=3,column=1,rowspan=3,sticky=E)
Button(root,text="上传",command=doSubmit).grid(row = 6,column = 1)
root.mainloop()虽然代码很短,但在整个过程中花费了较多的时间,是一个还不错的经历,故做记录,同时希望对类似需求的人有所帮助。