为LinkedIn编写一个简单的AI驱动铬扩展
#javascript #网络开发人员 #教程 #gpt3

介绍

改善用户体验并在浏览器中添加独特功能的一种绝妙方法是构建Chrome扩展名。它可能会添加一个特殊功能。人工智能技术的发展增长开辟了许多有益的生产力扩展。

在本教程中,我们将访问LinkedIn页面,并使用JavaScript和基本的Web刮擦提取文本。我们还将使用GPT-3对LinkedIn产生评论。我们将介绍镀铬扩展的关键元素,并构建一个简单的元素。本教程中未涵盖后端部分和及时的工程;相反,它们将分别覆盖。在这里,我们只能将现成的端点用作黑匣子。

如果要检查最终扩展工作,则可以检查视频:

开始编码之前要知道的事情

在开始编码之前,我们需要发现和理解的Chrome Extensions概念的几个重要部分。如果您知道您可以随意跳过该部分。

镀铬扩展的结构

Common chrome extension structure

Chrome Extensions具有一个共同的结构,其中包含应用程序的重要部分:

  • manifest.json-扩展名的清单是唯一具有特定文件名称的必需文件:subtest.json。它也必须在扩展名的根目录中。清单包含重要的元数据,定义资源,声明权限,并指定文件应在后台和页面上运行。

  • content.js-内容脚本是在网页上下文中运行的文件。通过使用标准文档对象模型(DOM),他们能够阅读浏览器访问的网页详细信息,对其进行更改,并将信息传递给其父级扩展。在docs中更多。在我们的情况下,我们将放置与内容相关的逻辑,例如解析LinkedIn页面,将自定义按钮添加到评论部分并提取帖子文本。

  • background.js-背景service worker。即使不打开扩展名的弹出窗口或选项页面,它也用于连续运行任务。在我们的情况下,该文件将处理扩展程序的背景过程,例如对后端端点进行API调用并处理响应。由于我们想提出API请求以解出自定义端点,因此我们需要与服务工作者分开执行此操作,以防止cross origin requests

也有一个弹出部分,它定义了Chrome扩展弹出的逻辑,我们将跳过,就像在我们的教程中一样,我们不需要任何弹出式UI和逻辑。

我们将文件styles.css添加到结构中,以使其变得无聊并改善UI部分。该文件为LinkedIn页面上扩展程序的其他元素提供了CSS样式。它将用于样式化评论部分添加的自定义按钮。

可以在official docs中找到有关铬扩展结构的更多详细信息。

subtest.json概述

manifest.json文件是chrome扩展名的配置文件,其中包含有关扩展名的重要信息,例如其名称,版本和所需的权限。在清单文件的版本3中,格式有几个新功能和更改,包括对扩展模块和服务工作者脚本的支持以及增加的安全措施。

manifest.json文件中最重要的部分之一是权限部分,其中扩展名指定正常运行所需的权限。例如,需要访问用户浏览历史记录的扩展名需要“历史记录”许可。在我们的情况下,我们只需要tabs许可即可在服务工作者和内容页面之间发送事件。
所有权限可以在official docs中检查。

进行API调用的另一个重要部分是
host_permissions。它包含一个或多个匹配模式,可访问指定的主机。在我们的情况下,我们在AWS上托管了后端端点,并且需要将其指定为https://*.amazonaws.com/*,以使与API交互成为可能。
注::最好提供最小使用*的特定模式,例如https://12345.amazonaws.com/*,因为我们最大程度地减少了安全问题的潜在表面。出于教程目的,我们简化了这一部分。

我们还需要指定内容脚本和样式文件路径,我们将使用将自定义逻辑和样式注入content_scripts部分的页面。

最后,我们有下一个manifest.json文件:

{
  "manifest_version": 3,
  "name": "LinkBuddy",
  "version": "0.0.1",
  "permissions": [
    "tabs"
  ],
  "host_permissions": [
    "https://*.amazonaws.com/*"
  ],
  "content_scripts": [
    {
      "matches": ["https://www.linkedin.com/*"],
      "css": ["styles.css"],
      "js": ["content.js"]
    }
  ],
  "action": {
    "default_title": "LinkBuddy"
  },
  "background": {
    "service_worker": "background.js"
  }
}

Chrome Extension中的事件消息传递

启动前要理解的另一个概念是message passing或事件消息传递。

事件消息传递是构建Chrome扩展的关键概念。它允许扩展名的不同部分(例如内容脚本和背景脚本)传达和交换数据。

在一个简单的示例中,假设我们在网站上有一个按钮,用户可以单击。该按钮在内容脚本中,当单击时,它将消息发送到背景脚本以执行特定操作。

单击按钮时,内容脚本使用chrome.runtime.sendMessage()函数将消息发送到背景脚本。该消息可以是包含任何需要传递数据的对象的形式。

document.getElementById("button").addEventListener("click", function() {
    // we send message of particular type "button-clicked"
    chrome.runtime.sendMessage({
        type: "button-clicked"
    });
});

在背景脚本中,我们使用chrome.runtime.onMessage.addListener函数聆听消息。收到消息后,我们可以根据消息中的数据执行所需的操作。

chrome.runtime.onMessage.addListener(
    // we listen to all messages and process them
    function(request, sender, sendResponse) {
        if (request.type === "button-clicked") {
            // Perform desired action
        }
    }
);

这只是一个简单的示例,但是事件消息传递可以在镀铬扩展中以多种方式使用。它可用于在扩展名的不同部分之间发送数据,触发操作,甚至将数据发送到服务器。关键是为消息及其数据使用一致的命名约定,以便扩展的不同部分可以理解它。

编写内容逻辑

一旦我们拥有设置扩展的结构,请在扩展中了解消息传递系统,并写出清单JSON,我们就很好。

Now we are ready

数据流概述

下面描述了扩展的数据流。以下是扩展程序数据流的描述。知道什么零件传递了什么数据以及它们之间的互动方式在设计应用程序时至关重要。

Data flow of the application

  1. 在页面上收听focusin事件。如果是评论部分,请添加3个按钮,其中包括事件OnClick事件处理程序。
  2. 单击按钮时,从帖子中收集文本数据,生成请求主体并将其作为generate-comment事件发送给背景工人,使用消息传递。
  3. 在背景工人上,请收听generate-comment事件,并用提供的身体进行API调用。将响应作为generate-comment-response事件发送给内容。
  4. generate-comment-response事件触发器上键入评论部分中的评论文本。

LinkedIn页面分析

要提取文本并识别评论部分以插入自定义按钮并注释我们需要在页面上发现它们并在此之后刮擦它们。要完成我们需要使用Chrome dev tools的元素选项卡。

Chrome dev tools

在Chrome的“检查”部分中,我们可以发现所有供稿页面分为相同的块,并将注释部分用于使用ql-editor类的自定义DIV。

HTML comment element

要提取特定评论的相关帖子部分,我们可以找到整个帖子的父容器,即带有feed-shared-update-v2类的div,并从div中提取div class feed-shared-update-v2__description-wrapper

content.js脚本

现在我们可以编写扩展名的一些代码。

Let's code

focusin活动

为了使我们的Chrome Application Interactive,我们需要实现从页面中提取元素并响应用户操作的逻辑。一种方法是使用事件听众。

我们需要使用focusin事件侦听器来检测用户何时单击页面上的特定元素。然后,我们可以提取该元素并执行一些操作,例如显示有关该元素或修改其属性的信息。

注意:重要的是要注意,为了在chrome扩展中使用事件听众,我们需要kude6在subtest.json.json.

中使用tabs

content.js中,我们需要添加代码:

document.addEventListener("focusin", function(event) {
    // the focused element is `event.target`
});

添加按钮

正如我们在LinkedIn页面分析中发现的那样,以确定焦点目标是评论部分,它应该是与ql-editor类的DIV。因此,我们需要添加到content.js

if (event.target.classList.contains("ql-editor")) {
    // process the div as a comment field
}

之后,我们可以添加3个按钮。在下面的代码示例中,我们仅添加一个按钮,但以相同的方式添加了另一个按钮。处理代码看起来像:

if (event.target.classList.contains("ql-editor")) {
    const parentForm = event.target.closest(".comments-comment-texteditor");

    if (parentForm && !parentForm.classList.contains("buttons-appended")) {
        // add appended class to add buttons only on the first event trigger
        parentForm.classList.add("buttons-appended");

        // create and append engage button
        let engageBtn = document.createElement("button");
        engageBtn.classList.add("rounded-button");
        engageBtn.innerText = "🤝 Engage";

        parentForm.appendChild(engageBtn);

        engageBtn.addEventListener("click", function(event) {
            processButtonClicked(event, "engage", parentForm);
        })
    } else {
        console.log("No parent with the class 'comments-comment-texteditor' found for the focused element.");
    }
}

我们拿出按钮:

Comment buttons without styling

让我们用样式添加一些美。

将样式添加到页面

要添加自定义样式,我们需要将附加的样式表链接附加到页面的头部部分。我们需要添加到content.js

let link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("href", chrome.runtime.getURL("styles.css"));
document.head.appendChild(link);

作为样式,我们在styles.css中具有按钮样式和简单的脉冲动画:

.rounded-button {
    border-width: 1px;
    /*border-color: #000000;*/
    border-color: rgba(0,0,0,0.3);
    border-style: solid;
    margin: 10px 3px 10px 3px;
    padding: 5px 10px 5px 10px;
    border-radius: 20px;
}

.first-rounded-button {
    margin-left: 10px;
}

.loading-animation {
    box-shadow: 0 0 0 0 rgb(247, 77, 77);
    transform: scale(1);
    animation: pulse_animation 2s infinite;
}

@keyframes pulse_animation {
    0% {
        transform: scale(0.95);
        box-shadow: 0 0 0 0 rgb(247, 77, 77);
    }

    70% {
        transform: scale(1);
        box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
    }

    100% {
        transform: scale(0.95);
        box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
    }
}

.disabled {
    opacity: 0.8;
    pointer-events: none;
}

结果我们将拥有这样的按钮:

New comment buttons

按钮单击处理

按钮单击processButtonClicked功能的处理手柄。

要避免同时请求,我们需要检查是否已单击的按钮处理程序正在运行。如果有的话,我们简单地返回而不采取任何行动来防止多个评论同时生成。另外,我们禁用页面上的所有其他按钮,以确保一次只能执行一项操作。

// global in content.js
let loading = false
// function processButtonClicked
if (loading) {
    console.log('already loading');

    return;
}

document.querySelectorAll(".rounded-button").forEach(function(button) {
    button.setAttribute("disabled", true);
    button.classList.add("disabled");
});

不要无聊,我们将脉冲动画添加到点击按钮,以向用户提供扩展名的视觉反馈。

event.currentTarget.classList.add("loading-animation");

然后,我们提取父帖的全文,然后删除可能存在的任何“查看更多”文本。本文将用于评论的一代。

// extract full text of the parent post
const parent = event.currentTarget.closest(".feed-shared-update-v2");
const elements = parent.getElementsByClassName("feed-shared-update-v2__description-wrapper")
let text = elements[0].innerText;
const textWithoutSeeMore = text.replace(/…see more/g, "");

最后,我们保存应用程序的当前状态,并将消息发送到背景脚本,传递按钮类型和父帖的文本。

// save current state of the app
loading = true
processButton = event.currentTarget
processParent = parentForm

// send the event
chrome.runtime.sendMessage({
    type: "generate-comment",
    buttonType: buttonType,
    event: event,
    parentForm: parentForm,
    text: textWithoutSeeMore,
});

该函数的最终代码是:

function processButtonClicked(event, buttonType, parentForm) {
    // check if we already loading the response
    if (loading) {
        console.log('already loading');

        return;
    }

    // disable all other buttons to avoid multiple comments creation simultaneously
    document.querySelectorAll(".rounded-button").forEach(function(button) {
        if (button.id !== "expertBtn") {
            button.setAttribute("disabled", true);
            button.classList.add("disabled");
        }
    });

    // add pulse animation to the clicked button
    event.currentTarget.classList.add("loading-animation");

    // extract full text of the parent post
    const parent = event.currentTarget.closest(".feed-shared-update-v2");
    const elements = parent.getElementsByClassName("feed-shared-update-v2__description-wrapper")
    let text = elements[0].innerText;
    const textWithoutSeeMore = text.replace(/…see more/g, "");

    // save current state of the app
    loading = true
    processButton = event.currentTarget
    processParent = parentForm

    // send the event
    chrome.runtime.sendMessage({
        type: "generate-comment",
        buttonType: buttonType,
        event: event,
        parentForm: parentForm,
        text: textWithoutSeeMore,
    });
}

background.js脚本

在背景中。

当事件侦听器接收“生成评论”消息时,它将带有消息的数据调用processGenerateCommentRequest函数。

chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
    switch (request.type) {
        // listen to the event
        case "generate-comment":
            await processGenerateCommentRequest(request);

            break;
        default:
            console.log('unknown request type', request.type);
    }
});

此功能通过帖子和按钮类型的文本向API端点发出帖子请求,处理响应并将结果发送回扩展名的前端。

async function processGenerateCommentRequest(request) {
    const config = {
        text: request.text,
        commentType: request.buttonType,
    }

    const requestOptions = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(config)
    };

    let response = {
        type: "generate-comment-response",
        error: "something went wrong",
    };
    try {
        // send API request
        let res = await fetch(`${apiBaseURL}/comment`, requestOptions);

        // process the results
        const results = await res.json()
        response = {
            type: "generate-comment-response",
            parentForm: request.parentForm,
            comment: results.results.comment,
        }
    } catch (error) {
        response = {
            type: "generate-comment-response",
            error: error,
        };
    }

    // send the event with response
    chrome.tabs.query({
        active: true,
        currentWindow: true
    }, function(tabs) {
        chrome.tabs.sendMessage(tabs[0].id, response, function(response) {
            console.log('send response', response)
        });
    });
}

content.js中收到评论并显示

构建我们的Chrome扩展程序的最后一步是在content.js脚本中接收事件并显示评论。

为此,我们使用chrome.runtime.onmessage.addlistener函数来聆听从其他脚本发送的消息。在这种情况下,我们正在聆听使用generate-comment-response类型的消息,我们从后台发送。

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    switch (request.type) {
        case "generate-comment-response":
            // stop loading process and enable all buttons
            loading = false;
            processButton.classList.remove("loading-animation");

            document.querySelectorAll(".rounded-button").forEach(function(button) {
                button.removeAttribute("disabled");
                button.classList.remove("disabled");
            });

            if (request.error) {
                console.error(request.error);

                return
            }

            emulateWriting(processParent, request.comment);

            break;
        default:
            console.log('unknown request type', request.type);
    }
});

之后,我们处理消息,如果成功地将其键入使用emulateWriting函数的注释部分。

function emulateWriting(parentElement, text) {
    let input = parentElement.querySelector(".ql-editor.ql-blank p");
    let i = 0;
    let interval = setInterval(() => {
        if (i < text.length) {
            input.innerText += text[i];
            i++;
            for (let j = 0; j < 10; j++) {
                if (i < text.length) {
                    input.innerText += text[i];
                    i++;
                }
            }
        } else {
            clearInterval(interval);
            // we need to remove `ql-blank` style from the section by LinkedIn div processing logic
            input.parentElement.classList.remove("ql-blank");
        }
    }, 10);
}

结论

我们已经解释了Chrome扩展的基本概念,并涵盖了使用GPT-3的力量生成评论的步骤。

使用类似的方法,我们可以对不同的社交媒体进行个性化处理,文章的摘要,其他类型的消息以及您可以考虑的任何内容。当然,响应的质量可能足够好,但是它主要用于一些评论的草稿,仍然需要首先检查人类。但是这样一个简单的项目的结果看起来很棒。

感谢您阅读本教程,如果您有任何疑问或反馈,请随时伸出手。跟着我阅读更多类似的教程文章ð€。

愉快的编码! ð»