226 lines
7.9 KiB
Lua
226 lines
7.9 KiB
Lua
---
|
||
--- 快速打开翻译,支持百度和有道翻译API
|
||
--- 有道API需要用户自行申请。
|
||
--- Created by sugood(https://github.com/sugood).
|
||
--- DateTime: 2020/10/23 11:12
|
||
---
|
||
|
||
|
||
local chooser = require("hs.chooser")
|
||
local menubar = hs.menubar.new()
|
||
local dictHistoryPath= "~/.hammerspoon/data/dictHistory.json"
|
||
local ydUrl = 'https://openapi.youdao.com/api'
|
||
local bdUrl = 'https://fanyi-api.baidu.com/api/trans/vip/translate'
|
||
local bdWeburl = 'https://fanyi.baidu.com/#'
|
||
local ydWeburl = 'http://youdao.com/w/'
|
||
|
||
local mChooser
|
||
local searchResult = {}
|
||
local isFirst = true
|
||
function truncate(q)
|
||
if q == nil or q == '' then
|
||
return ''
|
||
end
|
||
local len = SubStringGetTotalIndex(q)
|
||
if (len <= 20) then
|
||
return q
|
||
else
|
||
return SubStringUTF8(q,1,10)..len..SubStringUTF8(q,len-9)
|
||
end
|
||
end
|
||
|
||
function newWebview()
|
||
local cscreen = hs.screen.mainScreen()
|
||
local cres = cscreen:fullFrame()
|
||
sheetView = hs.webview.newBrowser({
|
||
x = cres.x+cres.w*0.15/2,
|
||
y = cres.y+cres.h*0.25/2,
|
||
w = cres.w*0.85,
|
||
h = cres.h*0.75
|
||
})
|
||
sheetView:windowTitle("CheatSheets")
|
||
sheetView:windowStyle("utility")
|
||
sheetView:titleVisibility("hidden")
|
||
sheetView:allowGestures(true)
|
||
sheetView:allowNewWindows(false)
|
||
sheetView:allowTextEntry(true)
|
||
sheetView:closeOnEscape(true)
|
||
sheetView:bringToFront(false)
|
||
sheetView:darkMode(true)
|
||
sheetView:reload(false)
|
||
sheetView:level(hs.drawing.windowLevels.mainMenu)
|
||
end
|
||
|
||
-- 右键弹出菜单
|
||
local rightClickCallbackFn = function(index)
|
||
if index and index > 0 then
|
||
local selectResult = mChooser:selectedRowContents(index)
|
||
if selectResult == nil or stringIsEmpty(selectResult.text) then
|
||
return
|
||
end
|
||
menubar = hs.menubar.new(false)
|
||
menubar:setTitle("Hidden Menu")
|
||
menubar:setMenu( {
|
||
{ title = "查看详情", fn = function()
|
||
hs.dialog.blockAlert(selectResult.text, selectResult.subText,"确定","","informational")
|
||
end },
|
||
{ title = "查看网页", fn = function()
|
||
sheetView:url(selectResult.webUrl):show()
|
||
end },
|
||
{ title = "-" },
|
||
{ title = "朗读译文", fn = function()
|
||
speak(selectResult.text)
|
||
end },
|
||
{ title = "朗读原文", fn = function()
|
||
speak(selectResult.subText)
|
||
end },
|
||
})
|
||
menubar:popupMenu(hs.mouse.getAbsolutePosition(), true)
|
||
end
|
||
end
|
||
|
||
-- 系统发音
|
||
function speak(str)
|
||
speaker = hs.speech.new()
|
||
speaker:speak(str)
|
||
end
|
||
|
||
function getBaiduApi(q)
|
||
headers = {ContentType = "application/x-www-form-urlencoded;charset=utf-8"}
|
||
salt = tostring(hs.timer.secondsSinceEpoch()*1000)
|
||
curtime = tostring(os.time())
|
||
geturl = "q="..q.."&from=auto&to=auto&appid=".. config[1].baiduAppid .."&salt="..salt.."&sign="..hs.hash.MD5(config[1].baiduAppid ..q..salt..config[1].baiduAppSecret)
|
||
--print('geturl:'..geturl)
|
||
|
||
hs.http.doAsyncRequest(bdUrl,'POST',geturl,headers,function(code, body, htable)
|
||
if code ~= 200 then
|
||
print('get weather error:'..code)
|
||
return
|
||
else
|
||
searchResult = {}
|
||
results = hs.json.decode(body)
|
||
print('results:'..hs.inspect(results))
|
||
if(results['trans_result']) then
|
||
item = {}
|
||
item.text = results['trans_result'][1].dst
|
||
item.subText = q
|
||
item.webUrl = bdWeburl..results['from'].."/"..results['to'].."/"..decodeURI(q)
|
||
table.insert(searchResult,1,item)
|
||
end
|
||
mChooser:choices(searchResult)
|
||
hs.json.write(searchResult,dictHistoryPath, true, true)
|
||
end
|
||
end)
|
||
end
|
||
|
||
function getYoudaoApi(q)
|
||
headers = {ContentType = "application/x-www-form-urlencoded;charset=utf-8"}
|
||
salt = tostring(hs.timer.secondsSinceEpoch()*1000)
|
||
curtime = tostring(os.time())
|
||
geturl = "q="..q.."&from=auto&to=auto&signType=v3&curtime="..curtime.."&appKey="..config[1].youdaoAppid..
|
||
"&salt="..salt.."&sign="..hs.hash.SHA256(config[1].youdaoAppid..truncate(q)..salt..curtime..config[1].youdaoAppSecret)
|
||
--print('geturl:'..geturl)
|
||
|
||
hs.http.doAsyncRequest(ydUrl,'POST',geturl,headers,function(code, body, htable)
|
||
if code ~= 200 then
|
||
print('get weather error:'..code)
|
||
return
|
||
else
|
||
searchResult = {}
|
||
results = hs.json.decode(body)
|
||
--print('results:'..hs.inspect(results))
|
||
if(results['web']) then
|
||
--逆序插入
|
||
for i=#results['web'],1,-1 do
|
||
item = {}
|
||
item.text = table.concat(results['web'][i].value,", ")
|
||
item.subText = results['web'][i].key
|
||
item.webUrl = ydWeburl..decodeURI(item.subText)
|
||
table.insert(searchResult,1,item)
|
||
end
|
||
end
|
||
if(results['basic']) then
|
||
if (results['basic']['exam_type']) then
|
||
item = {}
|
||
item.text = table.concat(results['basic']['exam_type'],', ')
|
||
item.subText = q
|
||
item.webUrl = ydWeburl..decodeURI(item.subText)
|
||
table.insert(searchResult,1,item)
|
||
end
|
||
if (results['basic']['explains']) then
|
||
for i=#results['basic']['explains'],1,-1 do
|
||
item = {}
|
||
item.text = results['basic']['explains'][i]
|
||
item.subText = q
|
||
item.webUrl = ydWeburl..decodeURI(item.subText)
|
||
table.insert(searchResult,1,item)
|
||
end
|
||
|
||
end
|
||
end
|
||
if(results['translation']) then
|
||
item = {}
|
||
item.text = table.concat(results['translation'])
|
||
item.subText = q
|
||
item.webUrl = ydWeburl..decodeURI(item.subText)
|
||
table.insert(searchResult,1,item)
|
||
end
|
||
mChooser:choices(searchResult)
|
||
hs.json.write(searchResult,dictHistoryPath, true, true)
|
||
end
|
||
end)
|
||
end
|
||
-- 粘贴选中的片段
|
||
local completionFn = function(result)
|
||
if result then
|
||
hs.pasteboard.setContents(result.text)
|
||
--hs.eventtap.keyStroke({ "cmd" }, "V")
|
||
print("keywords:"..result.text)
|
||
end
|
||
end
|
||
-- 监听输入框
|
||
local queryChangedCallbackFn = function(result)
|
||
print("changekeywords:"..result)
|
||
if result and #result > 0 then
|
||
if config[1].dictEngine == '百度' then
|
||
getBaiduApi(result)
|
||
else
|
||
getYoudaoApi(result)
|
||
end
|
||
else
|
||
if(isFirst) then
|
||
isFirst = false
|
||
else
|
||
searchResult = {}
|
||
mChooser:choices(searchResult)
|
||
hs.json.write(searchResult,dictHistoryPath, true, true)
|
||
end
|
||
end
|
||
end
|
||
|
||
function initData()
|
||
newWebview()
|
||
-- 选取片段内容(按下快捷键时显示片段列表,点击选中的快捷键将自动粘贴)
|
||
hs.hotkey.bind(hyperCmd, "F", function ()
|
||
isFirst = true
|
||
if hs.json.read(dictHistoryPath) ~= nil then
|
||
searchResult = hs.json.read(dictHistoryPath)
|
||
end
|
||
local num = hs.pasteboard.changeCount()
|
||
hs.eventtap.keyStroke({ "cmd" }, "C")
|
||
mChooser = chooser.new(completionFn)
|
||
:choices(searchResult)
|
||
:queryChangedCallback(queryChangedCallbackFn)
|
||
:rightClickCallback(rightClickCallbackFn)
|
||
:searchSubText(true)
|
||
:show()
|
||
local numAfter = hs.pasteboard.changeCount()
|
||
--如果复制成功则自动查询复制的内容,否者只打开翻译界面
|
||
if(numAfter > num) then
|
||
--粘贴选中的文本进行查询
|
||
hs.eventtap.keyStroke({ "cmd" }, "V")
|
||
end
|
||
end)
|
||
end
|
||
|
||
initData() |