Compare commits
10 Commits
4bb49a700e
...
c38ae42cc8
Author | SHA1 | Date |
---|---|---|
yutent | c38ae42cc8 | |
yutent | b160e1a7da | |
yutent | 6ef954b96c | |
yutent | ff8b6588d2 | |
宇天 | 94081982c3 | |
yutent | 50e150199e | |
宇天 | a89b162eed | |
宇天 | d05a8cad27 | |
宇天 | 8842df25dc | |
宇天 | e230fa13b3 |
21
Readme.md
21
Readme.md
|
@ -1,10 +1,10 @@
|
||||||
# 钉钉App - Linux版
|
# 钉钉App - Linux版
|
||||||
> `非官方版`
|
> `非官方版`
|
||||||
|
|
||||||
> 最新的3.8.7改用electron封装。
|
> 3.8.7起改用electron封装。
|
||||||
>> 因为最近钉钉取消了账号登录, 所以移除了之前记住账号的 注入脚本。
|
>> 因为最近钉钉取消了账号登录, 所以移除了之前记住账号的 注入脚本。
|
||||||
>> 注入改为缓存登录token, 然后在下次启动时,把token放出来, 以达到自动登录的效果。(token的有效期为15天, 这是钉钉自己设置的, 项目没有对它进行任何修改)
|
>> 注入改为缓存登录token, 然后在下次启动时,把token放出来, 以达到自动登录的效果。(token的有效期为15天, 这是钉钉自己设置的, 项目没有对它进行任何修改)
|
||||||
>> **`注意: 该注入不一定能用。`**
|
|
||||||
|
|
||||||
该修改版并不对钉钉现有功能进行任何的修改,也不收集任何信息。
|
该修改版并不对钉钉现有功能进行任何的修改,也不收集任何信息。
|
||||||
代码量很少, 而且完全开源, 请放心使用。
|
代码量很少, 而且完全开源, 请放心使用。
|
||||||
|
@ -14,8 +14,23 @@
|
||||||
![preview](./preview.png)
|
![preview](./preview.png)
|
||||||
|
|
||||||
|
|
||||||
|
## 更新日志
|
||||||
|
|
||||||
|
- 2022.04.26
|
||||||
|
1. 版本号改为`年.月.日`格式
|
||||||
|
2. 检测未读消息, 如有, 托盘图标显示红点。
|
||||||
|
|
||||||
|
- 2022.04.24
|
||||||
|
1. electron升级为18.0
|
||||||
|
2. 修复自动登录功能
|
||||||
|
3. 禁用`nodejs`, 注入更为安全
|
||||||
|
4. 包名修改为`top.yutent.dtalk`, 之前有安装过旧版的, 请先卸载
|
||||||
|
5. 不再打包`AppImage`版本, 其他非`debian`系发行版的, 请自行编译
|
||||||
|
|
||||||
|
|
||||||
## 打包好的安装包
|
## 打包好的安装包
|
||||||
请进入`release`页面下载.
|
|
||||||
|
下载地址请转到`release`页面
|
||||||
|
|
||||||
|
|
||||||
## 自行打包
|
## 自行打包
|
||||||
|
|
24
package.json
24
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "dtalk",
|
"name": "dtalk",
|
||||||
"version": "3.8.7",
|
"version": "22.4.26",
|
||||||
"description": "钉钉-Linux版",
|
"description": "钉钉-Linux版",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -9,31 +9,33 @@
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "yutent",
|
"name": "yutent",
|
||||||
"email": "yutent@doui.cc"
|
"email": "yutent.io@gmail.com"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^6.0.0",
|
"electron": "^18.0.0",
|
||||||
"electron-builder": "^21.2.0"
|
"electron-builder": "^23.0.0"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "cc.doui.dtalk",
|
"appId": "top.yutent.dtalk",
|
||||||
"productName": "DingDing",
|
"productName": "钉钉(DingTalk)",
|
||||||
|
"electronDownload": {
|
||||||
|
"version": "18.1.0",
|
||||||
|
"mirror": "https://npm.taobao.org/mirrors/electron/"
|
||||||
|
},
|
||||||
"copyright": "Copyright © 2019 ${author}",
|
"copyright": "Copyright © 2019 ${author}",
|
||||||
"directories": {
|
"directories": {
|
||||||
"buildResources": "icons",
|
"buildResources": "icons",
|
||||||
"output": "build"
|
"output": "build"
|
||||||
},
|
},
|
||||||
"files": ["src/**/*"],
|
"files": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
"linux": {
|
"linux": {
|
||||||
"category": "Network;Chat",
|
"category": "Network;Chat",
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "deb",
|
"target": "deb",
|
||||||
"arch": "x64"
|
"arch": "x64"
|
||||||
},
|
|
||||||
{
|
|
||||||
"target": "AppImage",
|
|
||||||
"arch": "x64"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "./icons/"
|
"icon": "./icons/"
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
67
src/main.js
67
src/main.js
|
@ -1,13 +1,15 @@
|
||||||
/* app */
|
/* app */
|
||||||
const { app, session, Menu } = require('electron')
|
const { app, session, Menu, ipcMain } = require('electron')
|
||||||
const path = require('path')
|
const { join } = require('path')
|
||||||
|
const fs = require('fs')
|
||||||
const log = console.log
|
|
||||||
|
|
||||||
const createTray = require('./tools/tray')
|
const createTray = require('./tools/tray')
|
||||||
const { createMainWindow } = require('./tools/windows')
|
const { createMainWindow } = require('./tools/windows')
|
||||||
|
|
||||||
const ROOT = __dirname
|
const ROOT = __dirname
|
||||||
|
const APP_DIR = join(app.getPath('appData'), './dtalk')
|
||||||
|
const sessionFile = join(APP_DIR, './login_session.json')
|
||||||
|
const cookieFile = join(APP_DIR, './login_cookie.json')
|
||||||
|
|
||||||
/* ----------------------------------------------------- */
|
/* ----------------------------------------------------- */
|
||||||
|
|
||||||
|
@ -22,10 +24,61 @@ Menu.setApplicationMenu(null)
|
||||||
app.once('ready', () => {
|
app.once('ready', () => {
|
||||||
// 修改app的UA
|
// 修改app的UA
|
||||||
session.defaultSession.setUserAgent(
|
session.defaultSession.setUserAgent(
|
||||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
|
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36'
|
||||||
)
|
)
|
||||||
|
|
||||||
let win = createMainWindow(path.join(ROOT, './images/app.png'))
|
let win = createMainWindow(join(ROOT, './images/app.png'))
|
||||||
|
|
||||||
createTray(win)
|
app.toggleTray = createTray(win)
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('app', (ev, conn) => {
|
||||||
|
switch (conn.type) {
|
||||||
|
case 'saveToken':
|
||||||
|
fs.writeFile(sessionFile, conn.data, function (err) {})
|
||||||
|
ev.returnValue = true
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'readToken':
|
||||||
|
{
|
||||||
|
let cache = ''
|
||||||
|
try {
|
||||||
|
cache = fs.readFileSync(sessionFile).toString()
|
||||||
|
} catch (err) {}
|
||||||
|
ev.returnValue = cache
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'restoreCookie':
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
let cache = fs.readFileSync(cookieFile).toString()
|
||||||
|
cache = JSON.parse(cache)
|
||||||
|
for (let it of cache) {
|
||||||
|
it.url = 'https://im.dingtalk.com'
|
||||||
|
|
||||||
|
session.defaultSession.cookies.set(it)
|
||||||
|
}
|
||||||
|
} catch (err) {}
|
||||||
|
ev.returnValue = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'saveCookie':
|
||||||
|
{
|
||||||
|
let cookie = session.defaultSession.cookies.get({})
|
||||||
|
cookie.then(r => {
|
||||||
|
fs.writeFile(cookieFile, JSON.stringify(r), function (err) {})
|
||||||
|
})
|
||||||
|
ev.returnValue = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'toggleTray':
|
||||||
|
{
|
||||||
|
app.toggleTray(conn.data)
|
||||||
|
ev.returnValue = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
const { ipcRenderer, shell, contextBridge } = require('electron')
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('electron', {
|
||||||
|
open(url) {
|
||||||
|
shell.openExternal(url)
|
||||||
|
},
|
||||||
|
saveToken(id, token) {
|
||||||
|
token = JSON.parse(token)
|
||||||
|
// token.isAutoLogin = true
|
||||||
|
token = JSON.stringify(token)
|
||||||
|
|
||||||
|
ipcRenderer.sendSync('app', {
|
||||||
|
data: JSON.stringify({ id, token }),
|
||||||
|
type: 'saveToken'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
readToken() {
|
||||||
|
return ipcRenderer.sendSync('app', { type: 'readToken' })
|
||||||
|
},
|
||||||
|
|
||||||
|
restoreCookie() {
|
||||||
|
return ipcRenderer.sendSync('app', { type: 'restoreCookie' })
|
||||||
|
},
|
||||||
|
|
||||||
|
saveCookie() {
|
||||||
|
return ipcRenderer.sendSync('app', { type: 'saveCookie' })
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleTray(unread) {
|
||||||
|
return ipcRenderer.sendSync('app', { type: 'toggleTray', data: unread })
|
||||||
|
}
|
||||||
|
})
|
|
@ -1,17 +1,18 @@
|
||||||
/**
|
/**
|
||||||
* 托盘
|
* 托盘
|
||||||
* @author yutent<yutent@doui.cc>
|
* @author yutent<yutent.io@gmail.com>
|
||||||
* @date 2019/01/21 20:42:07
|
* @date 2019/01/21 20:42:07
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { app, Tray, Menu } = require('electron')
|
const { app, Tray, Menu } = require('electron')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const ROOT = __dirname
|
const ROOT = __dirname
|
||||||
|
|
||||||
module.exports = function(win) {
|
const TRAY_ICO = path.join(ROOT, '../images/tray.png')
|
||||||
app.__TRAY__ = new Tray(path.join(ROOT, '../images/tray.png'))
|
const TRAY_ICO_A = path.join(ROOT, '../images/tray_a.png')
|
||||||
|
|
||||||
|
module.exports = function (win) {
|
||||||
|
let dTray = new Tray(TRAY_ICO)
|
||||||
let menuList = Menu.buildFromTemplate([
|
let menuList = Menu.buildFromTemplate([
|
||||||
{
|
{
|
||||||
label: '显示主窗口',
|
label: '显示主窗口',
|
||||||
|
@ -27,9 +28,25 @@ module.exports = function(win) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
app.__TRAY__.on('click', _ => {
|
let unreadCache = false
|
||||||
|
|
||||||
|
dTray.on('click', _ => {
|
||||||
win.show()
|
win.show()
|
||||||
})
|
})
|
||||||
|
|
||||||
app.__TRAY__.setContextMenu(menuList)
|
dTray.setContextMenu(menuList)
|
||||||
|
|
||||||
|
return function (unread) {
|
||||||
|
// 缓存状态, 避免频繁修改tray图标
|
||||||
|
if (unreadCache === unread) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unreadCache = unread
|
||||||
|
|
||||||
|
if (unread) {
|
||||||
|
dTray.setImage(TRAY_ICO_A)
|
||||||
|
} else {
|
||||||
|
dTray.setImage(TRAY_ICO)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
/**
|
/**
|
||||||
* 各种窗口创建
|
* 各种窗口创建
|
||||||
* @author yutent<yutent@doui.cc>
|
* @author yutent<yutent.io@gmail.com>
|
||||||
* @date 2019/01/26 18:28:22
|
* @date 2019/01/26 18:28:22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
const { join } = require('path')
|
||||||
|
|
||||||
const { BrowserWindow } = require('electron')
|
const { BrowserWindow } = require('electron')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用主窗口
|
* 应用主窗口
|
||||||
*/
|
*/
|
||||||
exports.createMainWindow = function(icon) {
|
exports.createMainWindow = function (icon) {
|
||||||
// 创建浏览器窗口
|
// 创建浏览器窗口
|
||||||
let win = new BrowserWindow({
|
let win = new BrowserWindow({
|
||||||
title: '钉钉-electron版',
|
title: '钉钉-electron版',
|
||||||
|
@ -23,14 +22,12 @@ exports.createMainWindow = function(icon) {
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
webSecurity: false,
|
webSecurity: false,
|
||||||
experimentalFeatures: true,
|
experimentalFeatures: true,
|
||||||
nodeIntegration: true
|
// nodeIntegration: true,
|
||||||
|
preload: join(__dirname, './inject.js')
|
||||||
},
|
},
|
||||||
show: false
|
show: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// 然后加载应用的 index.html。
|
|
||||||
|
|
||||||
// win.loadURL('app://local/index.html')
|
|
||||||
win.loadURL('https://im.dingtalk.com')
|
win.loadURL('https://im.dingtalk.com')
|
||||||
|
|
||||||
win.on('ready-to-show', _ => {
|
win.on('ready-to-show', _ => {
|
||||||
|
@ -42,32 +39,54 @@ exports.createMainWindow = function(icon) {
|
||||||
win.webContents.on('dom-ready', ev => {
|
win.webContents.on('dom-ready', ev => {
|
||||||
win.webContents.executeJavaScript(
|
win.webContents.executeJavaScript(
|
||||||
`
|
`
|
||||||
|
|
||||||
const shell = require('electron').shell;
|
|
||||||
|
|
||||||
// assuming $ is jQuery
|
|
||||||
$(document).on('click', 'a[href^="http"]', function(event) {
|
$(document).on('click', 'a[href^="http"]', function(event) {
|
||||||
|
if(!this.hasAttribute('nwdirectory')){
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
shell.openExternal(this.href);
|
electron.open(this.href)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
localStorage.setItem("isBeepOpen", "true");
|
localStorage.setItem("isBeepOpen", "true");
|
||||||
localStorage.setItem("notification", "true");
|
localStorage.setItem("notification", "true");
|
||||||
localStorage.setItem("newUserState", "secTip");
|
localStorage.setItem("newUserState", "secTip");
|
||||||
localStorage.setItem("latest_lang_info", "zh_CN");
|
localStorage.setItem("latest_lang_info", "zh_CN");
|
||||||
|
localStorage.setItem("login_method", "autoLogin");
|
||||||
|
|
||||||
if(localStorage.getItem('fuck2')){
|
|
||||||
|
|
||||||
sessionStorage.setItem('wk_device_id', localStorage.getItem('fuck1'))
|
let cache = electron.readToken()
|
||||||
sessionStorage.setItem('wk_token', localStorage.getItem('fuck2'))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if(cache){
|
||||||
|
cache = JSON.parse(cache)
|
||||||
|
sessionStorage.setItem('wk_device_id', cache.id)
|
||||||
|
sessionStorage.setItem('wk_token', cache.token)
|
||||||
|
|
||||||
|
electron.restoreCookie()
|
||||||
|
|
||||||
|
// 第一次进来刷新一下页面, 才会自动登录
|
||||||
if(!sessionStorage.getItem('first_in')){
|
if(!sessionStorage.getItem('first_in')){
|
||||||
sessionStorage.setItem('first_in', 1)
|
sessionStorage.setItem('first_in', 1)
|
||||||
|
setTimeout(function(){
|
||||||
location.reload()
|
location.reload()
|
||||||
|
}, 2000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkUnReadMsg() {
|
||||||
|
|
||||||
|
window.__timer = setTimeout(function() {
|
||||||
|
var $box = document.body.querySelector('.conv-lists-box')
|
||||||
|
var $unread = $box.querySelectorAll('.unread-num')
|
||||||
|
|
||||||
|
electro($unread.length > 0)
|
||||||
|
clearTimeout(window.__timer)
|
||||||
|
checkUnReadMsg()
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkUnReadMsg()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
`,
|
`,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
@ -79,9 +98,12 @@ exports.createMainWindow = function(icon) {
|
||||||
`
|
`
|
||||||
if(sessionStorage.getItem('wk_token')){
|
if(sessionStorage.getItem('wk_token')){
|
||||||
|
|
||||||
localStorage.setItem('fuck1', sessionStorage.getItem('wk_device_id'))
|
electron.saveCookie()
|
||||||
|
|
||||||
localStorage.setItem('fuck2', sessionStorage.getItem('wk_token'))
|
electron.saveToken(
|
||||||
|
sessionStorage.getItem('wk_device_id'),
|
||||||
|
sessionStorage.getItem('wk_token')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
`,
|
`,
|
||||||
|
|
Reference in New Issue