From 984ff6cfce517f15d884f8772510616c9000272d Mon Sep 17 00:00:00 2001 From: woni <320753691@qq.com> Date: Tue, 5 Sep 2023 22:48:20 +0800 Subject: [PATCH] fisrt commit --- README.md | 3 + app/content-script.js | 27 ++++++ app/index.html | 21 +++++ app/index.js | 86 ++++++++++++++++++ app/manifest.json | 52 +++++++++++ app/sw.js | 24 +++++ app/wps.js | 69 +++++++++++++++ host/botext.bat | 4 + host/client.py | 19 ++++ host/com.google.chrome.botext.json | 9 ++ host/install_host.bat | 7 ++ host/native-messaging-example-host | 82 +++++++++++++++++ host/native-messaging-example-host copy | 112 ++++++++++++++++++++++++ host/server.py | 55 ++++++++++++ host/uninstall_host.bat | 7 ++ 15 files changed, 577 insertions(+) create mode 100644 README.md create mode 100644 app/content-script.js create mode 100644 app/index.html create mode 100644 app/index.js create mode 100644 app/manifest.json create mode 100644 app/sw.js create mode 100644 app/wps.js create mode 100644 host/botext.bat create mode 100644 host/client.py create mode 100644 host/com.google.chrome.botext.json create mode 100644 host/install_host.bat create mode 100644 host/native-messaging-example-host create mode 100644 host/native-messaging-example-host copy create mode 100644 host/server.py create mode 100644 host/uninstall_host.bat diff --git a/README.md b/README.md new file mode 100644 index 0000000..caac5d9 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# BotExt + +Chrome 插件 diff --git a/app/content-script.js b/app/content-script.js new file mode 100644 index 0000000..434a7ff --- /dev/null +++ b/app/content-script.js @@ -0,0 +1,27 @@ + +// declare message listener from inject script +window.addEventListener('message', (event) => { + if (event.source === window && event.data.from === 'inject-script') { + console.log('[Demo] receive', event.data); + + } +}); + + + +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + + + + console.log('[Demo] =============================================='); + + const func = request.func; + if (func === 'addRecord') { + window.postMessage({ msg: 'ping from content script', from: 'content-script', func: 'addRecord' }); + } + if (func === 'deleteRecord') { + window.postMessage({ msg: 'ping from content script', from: 'content-script', func: 'deleteRecord' }); + } + console.log('[Demo] receive', func); + sendResponse({ response: '[Demo] pone-pone from content script' }); +}); diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..1715aa3 --- /dev/null +++ b/app/index.html @@ -0,0 +1,21 @@ + + + + + + WpsDbSheet + + + +
+ + +
+
+ + + +
+ + + diff --git a/app/index.js b/app/index.js new file mode 100644 index 0000000..7026a25 --- /dev/null +++ b/app/index.js @@ -0,0 +1,86 @@ +const DYNAMIC_SCRIPT_ID = 'add-record-script'; + +async function isDynamicContentScriptRegistered() { + const scripts = await chrome.scripting.getRegisteredContentScripts(); + return scripts.some((s) => s.id === DYNAMIC_SCRIPT_ID); +} + +async function getActiveTab() { + const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true }); + return tab; +} + + +document.querySelector('#addRecord').addEventListener('click', async () => { + + console.log('[Demo] =============================================='); + console.log('[Demo] Test send message from popup script to content script'); + const tab = await getActiveTab(); + const ret = await chrome.tabs.sendMessage(tab.id, { msg: 'ping from popup script', func: 'addRecord' }); + console.log('[Demo] receive', ret); + +}); + + +document.querySelector('#deleteRecord').addEventListener('click', async () => { + + console.log('[Demo] =============================================='); + console.log('[Demo] Test send message from popup script to content script'); + const tab = await getActiveTab(); + const ret = await chrome.tabs.sendMessage(tab.id, { msg: 'ping from popup script', func: 'deleteRecord' }); + console.log('[Demo] receive', ret); + +}); + + +var port = null; + +function appendMessage(text) { + document.getElementById('response').innerHTML += "

" + text + "

"; +} + +function updateUiState() { + if (port) { + document.getElementById('connect-button').style.display = 'none'; + document.getElementById('input-text').style.display = 'block'; + document.getElementById('send-message-button').style.display = 'block'; + } else { + document.getElementById('connect-button').style.display = 'block'; + document.getElementById('input-text').style.display = 'none'; + document.getElementById('send-message-button').style.display = 'none'; + } +} + +function sendNativeMessage() { + message = { "text": document.getElementById('input-text').value }; + port.postMessage(message); + appendMessage("Sent message: " + JSON.stringify(message) + ""); +} + +function onNativeMessage(message) { + appendMessage("Received message: " + JSON.stringify(message) + ""); +} + +function onDisconnected() { + appendMessage("Failed to connect: " + chrome.runtime.lastError.message); + port = null; + updateUiState(); +} + +function connect() { + var hostName = "com.google.chrome.botext"; + appendMessage("Connecting to native messaging host " + hostName + "") + port = chrome.runtime.connectNative(hostName); + port.onMessage.addListener(onNativeMessage); + port.onDisconnect.addListener(onDisconnected); + updateUiState(); +} + +document.addEventListener('DOMContentLoaded', function () { + document.getElementById('connect-button').addEventListener( + 'click', connect); + document.getElementById('send-message-button').addEventListener( + 'click', sendNativeMessage); + updateUiState(); +}); + diff --git a/app/manifest.json b/app/manifest.json new file mode 100644 index 0000000..0c3a06d --- /dev/null +++ b/app/manifest.json @@ -0,0 +1,52 @@ +{ + "name": "BotExt", + "version": "0.1.0", + "manifest_version": 3, + "background": { + "service_worker": "sw.js" + }, + "content_scripts": [ + { + "matches": [ + "https://www.kdocs.cn/*" + ], + "js": [ + "content-script.js" + ], + "run_at": "document_end" + }, + { + "matches": [ + "" + ], + "js": [ + "wps.js" + ], + "run_at": "document_end", + "world": "MAIN" + } + ], + "permissions": [ + "scripting", + "webNavigation", + "storage", + "activeTab", + "nativeMessaging" + ], + "host_permissions": [ + "https://www.kdocs.cn/*" + ], + "action": { + "default_popup": "index.html" + }, + "web_accessible_resources": [ + { + "resources": [ + "wps.js" + ], + "matches": [ + "" + ] + } + ] +} diff --git a/app/sw.js b/app/sw.js new file mode 100644 index 0000000..8e493ad --- /dev/null +++ b/app/sw.js @@ -0,0 +1,24 @@ +function injectedFunction(color) { + document.body.style.backgroundColor = color; +} + + +chrome.runtime.onMessage.addListener(async ({ name, options }) => { + if (name === 'addRecord') { + console.log('Hello World!'); + + // get active tab + // const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + // chrome.scripting.executeScript({ + // target: { tabId: tab.id }, + // func: injectedFunction, + // args: ["orange"], + // }); + + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + const response = await chrome.tabs.sendMessage(tab.id, { greeting: "hello" }); + // do something with response here, not outside the function + console.log(response); + + } +}); diff --git a/app/wps.js b/app/wps.js new file mode 100644 index 0000000..91dca1c --- /dev/null +++ b/app/wps.js @@ -0,0 +1,69 @@ + +async function createApp() { + for (let i = 0; i < 10; i++) { + + try { + window.$dbSheet = {}; + // set dbsheet app 属性 为 WPSOpenApi.Application; + window.$dbSheet.app = WPSOpenApi.Application; + + $dbSheet.createRecords = () => { + const records = $dbSheet.app.Record.CreateRecords({ + SheetId: 1, + Records: [{ fields: { "名称": "demo", "数量": 1, "日期": "2022/1/1", "状态": "未开始" } }] + }); + } + + $dbSheet.deleteRecords = async () => { + + const records = await $dbSheet.app.Record.GetRecords({ SheetId: 1 }) + const deleteRcecordId = records.records[0].id + const result = $dbSheet.app.Record.DeleteRecords({ + SheetId: 1, + RecordIds: [deleteRcecordId] + }) + } + console.log(i); + } catch (error) { + // sleep 1s + await new Promise(resolve => setTimeout(resolve, 1000)); + console.log(i, error); + } + } +} + +// 异步执行 createApp + +createApp().then(() => { }); + +window.addEventListener('message', (event) => { + if (event.source === window && event.data.func === 'addRecord') { + // console.log('[Demo] receive', event.data); + addRecordOnMessage(); + } + + if (event.source === window && event.data.func === 'deleteRecord') { + // console.log('[Demo] receive', event.data); + deleteRecordOnMessage(); + } +}); + + +async function addRecordOnMessage() { + $dbSheet.createRecords(); + window.postMessage({ msg: 'add record by inject script', from: 'inject-script' }); +} + + +async function deleteRecordOnMessage() { + await $dbSheet.deleteRecords(); + window.postMessage({ msg: 'delete record by inject script', from: 'inject-script' }); +} + + +async function sendMsgToContentScript() { + console.log('[Demo] =============================================='); + console.log('[Demo] Test send message from inject script to content script'); + $dbSheet.createRecords(); + window.postMessage({ msg: 'ping from inject script', from: 'inject-script' }); +} diff --git a/host/botext.bat b/host/botext.bat new file mode 100644 index 0000000..13a64f1 --- /dev/null +++ b/host/botext.bat @@ -0,0 +1,4 @@ +@echo off + + +python "%~dp0/native-messaging-example-host" %* diff --git a/host/client.py b/host/client.py new file mode 100644 index 0000000..634982e --- /dev/null +++ b/host/client.py @@ -0,0 +1,19 @@ +import win32pipe +import win32file + +# 连接到命名管道 +pipe_name = r'\\.\pipe\botext' +pipe = win32file.CreateFile( + pipe_name, + win32file.GENERIC_READ | win32file.GENERIC_WRITE, + 0, + None, + win32file.OPEN_EXISTING, + 0, + None +) + +win32file.WriteFile(pipe, b'close1') + +# 关闭管道 +win32file.CloseHandle(pipe) diff --git a/host/com.google.chrome.botext.json b/host/com.google.chrome.botext.json new file mode 100644 index 0000000..6bb7b9e --- /dev/null +++ b/host/com.google.chrome.botext.json @@ -0,0 +1,9 @@ +{ + "name": "com.google.chrome.botext", + "description": "botext", + "path": "D:\\rust\\botext\\target\\debug\\botext.exe", + "type": "stdio", + "allowed_origins": [ + "chrome-extension://hbneoijjlheddhhonbkamdmgljmjcdom/" + ] +} diff --git a/host/install_host.bat b/host/install_host.bat new file mode 100644 index 0000000..6b4de40 --- /dev/null +++ b/host/install_host.bat @@ -0,0 +1,7 @@ +:: Copyright 2014 The Chromium Authors. All rights reserved. +:: Use of this source code is governed by a BSD-style license that can be +:: found in the LICENSE file. + +:: Change HKCU to HKLM if you want to install globally. +:: %~dp0 is the directory containing this bat script and ends with a backslash. +REG ADD "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.google.chrome.botext" /ve /t REG_SZ /d "%~dp0com.google.chrome.botext.json" /f diff --git a/host/native-messaging-example-host b/host/native-messaging-example-host new file mode 100644 index 0000000..f14e005 --- /dev/null +++ b/host/native-messaging-example-host @@ -0,0 +1,82 @@ +import sys +import struct +import ctypes +import threading +import win32pipe +import win32file + + +pipe_name = r"\\.\pipe\botext" +pipe_access = win32pipe.PIPE_ACCESS_DUPLEX +pipe_type =win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT | win32pipe.PIPE_READMODE_MESSAGE +pipe_mode = win32pipe.PIPE_UNLIMITED_INSTANCES +max_instances = 65536 +out_buffer_size = 65536 +default_time_out = 0 +security_attributes = None + + +pipe = win32pipe.CreateNamedPipe( + pipe_name, + pipe_access, + pipe_type, + pipe_mode, + max_instances, + out_buffer_size, + default_time_out, + security_attributes, +) + + +def write_data(data): + msglen = len(data) + msglen_bytes = struct.pack("=I", msglen) + sys.stdout.buffer.write(msglen_bytes) + sys.stdout.buffer.write(data.encode()) + sys.stdout.buffer.flush() + + +def read_thread_func(): + while True: + raw_msglen = sys.stdin.buffer.read(4) + if not raw_msglen: + sys.exit(0) + msglen = struct.unpack("=I", raw_msglen)[0] + + # Read the message data + data = sys.stdin.buffer.read(msglen) + ctypes.windll.user32.MessageBoxW(0, data.decode(), "BotExt", 0) + write_data('{"msg": "bridge"}') + + +def namepipe_server(pipe): + try: + while True: + win32pipe.ConnectNamedPipe(pipe, None) + _, data = win32file.ReadFile(pipe, 4096) + if data == b"close": + sys.exit(0) + print(data) + write_data('{"msg": "client"}') + # ctypes.windll.user32.MessageBoxW(0, "hi", "BotExt", 0) + win32pipe.DisconnectNamedPipe(pipe) + except Exception as e: + win32pipe.DisconnectNamedPipe(pipe) + finally: + win32file.CloseHandle(pipe) + + +def namepipe_server_forver(pipe): + while True: + namepipe_server(pipe) + + +if __name__ == "__main__": + thread = threading.Thread(target=read_thread_func) + thread2 = threading.Thread(target=namepipe_server_forver, args=(pipe,)) + thread.daemon = True + thread2.daemon = True + thread.start() + thread2.start() + thread.join() + thread2.join() diff --git a/host/native-messaging-example-host copy b/host/native-messaging-example-host copy new file mode 100644 index 0000000..5543a4e --- /dev/null +++ b/host/native-messaging-example-host copy @@ -0,0 +1,112 @@ +import struct +import sys +import threading +from queue import Queue + + +import tkinter as Tkinter +from tkinter import messagebox as tkMessageBox + +if sys.platform == "win32": + import os, msvcrt + + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + + +def send_message(message): + sys.stdout.write(str(len(message))) + sys.stdout.write(message) + sys.stdout.flush() + + +def read_thread_func(queue): + message_number = 0 + while 1: + text_length_bytes = sys.stdin.read(4) + if len(text_length_bytes) == 0: + if queue: + queue.put(None) + sys.exit(0) + + text_length = struct.unpack("i", text_length_bytes)[0] + text = sys.stdin.read(text_length).decode("utf-8") + + if queue: + queue.put(text) + else: + send_message('{"echo": %s}' % text) + + +if Tkinter: + + class NativeMessagingWindow(Tkinter.Frame): + def __init__(self, queue): + self.queue = queue + + Tkinter.Frame.__init__(self) + self.pack() + + self.text = Tkinter.Text(self) + self.text.grid(row=0, column=0, padx=10, pady=10, columnspan=2) + self.text.config(state=Tkinter.DISABLED, height=10, width=40) + + self.messageContent = Tkinter.StringVar() + self.sendEntry = Tkinter.Entry(self, textvariable=self.messageContent) + self.sendEntry.grid(row=1, column=0, padx=10, pady=10) + + self.sendButton = Tkinter.Button(self, text="Send", command=self.onSend) + self.sendButton.grid(row=1, column=1, padx=10, pady=10) + + self.after(100, self.processMessages) + + def processMessages(self): + while not self.queue.empty(): + message = self.queue.get_nowait() + if message == None: + self.quit() + return + self.log("Received %s" % message) + + self.after(100, self.processMessages) + + def onSend(self): + text = '{"text": "' + self.messageContent.get() + '"}' + self.log("Sending %s" % text) + try: + send_message(text) + except IOError: + tkMessageBox.showinfo( + "Native Messaging Example", "Failed to send message." + ) + sys.exit(1) + + def log(self, message): + self.text.config(state=Tkinter.NORMAL) + self.text.insert(Tkinter.END, message + "\n") + self.text.config(state=Tkinter.DISABLED) + + +def Main(): + if not Tkinter: + send_message( + "\"Tkinter python module wasn't found. Running in headless " + + 'mode. Please consider installing Tkinter."' + ) + read_thread_func(None) + sys.exit(0) + + queue = Queue() + + main_window = NativeMessagingWindow(queue) + main_window.master.title("Native Messaging Example") + + thread = threading.Thread(target=read_thread_func, args=(queue,)) + thread.daemon = True + thread.start() + main_window.mainloop() + sys.exit(0) + + +if __name__ == "__main__": + Main() diff --git a/host/server.py b/host/server.py new file mode 100644 index 0000000..f720306 --- /dev/null +++ b/host/server.py @@ -0,0 +1,55 @@ +# python windows namepipe serve + +import win32pipe, win32file +import json +import threading +import sys +import ctypes + +# 连接到命名管道 +pipe_name = r"\\.\pipe\botext" +pipe_access = win32pipe.PIPE_ACCESS_DUPLEX +pipe_type = win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT +pipe_mode = 1 +max_instances = 65536 +out_buffer_size = 65536 +default_time_out = 0 +security_attributes = None + +pipe = win32pipe.CreateNamedPipe( + pipe_name, + win32pipe.PIPE_ACCESS_DUPLEX, + win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT | win32pipe.PIPE_READMODE_MESSAGE, + win32pipe.PIPE_UNLIMITED_INSTANCES, + 65535, + 65535, + 0, + None, +) + + +def namepipe_server(pipe): + try: + while True: + win32pipe.ConnectNamedPipe(pipe, None) + _, data = win32file.ReadFile(pipe, 4096) + if data == b'close': + sys.exit(0) + print(data) + ctypes.windll.user32.MessageBoxW(0, "hi", "BotExt", 0) + win32pipe.DisconnectNamedPipe(pipe) + except Exception as e: + win32pipe.DisconnectNamedPipe(pipe) + finally: + win32file.CloseHandle(pipe) + +def namepipe_server_forver(pipe): + while True: + namepipe_server(pipe) + + +if __name__ == "__main__": + task = threading.Thread(target=namepipe_server, args=(pipe,)) + task.daemon = True + task.start() + task.join() diff --git a/host/uninstall_host.bat b/host/uninstall_host.bat new file mode 100644 index 0000000..ca15585 --- /dev/null +++ b/host/uninstall_host.bat @@ -0,0 +1,7 @@ +:: Copyright 2014 The Chromium Authors. All rights reserved. +:: Use of this source code is governed by a BSD-style license that can be +:: found in the LICENSE file. + +:: Deletes the entry created by install_host.bat +REG DELETE "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.google.chrome.botext" /f +REG DELETE "HKLM\Software\Google\Chrome\NativeMessagingHosts\com.google.chrome.botext" /f