Instagram - 为用户添加备注(别名/标签)

为用户添加备注(别名/标签)功能,以帮助识别和搜索

当前为 2023-02-27 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name                Instagram - Add notes to the user
// @name:zh-CN          Instagram - 为用户添加备注(别名/标签)
// @name:zh-TW          Instagram - 為使用者新增備註(別名/標籤)
// @namespace           https://greasyfork.org/zh-CN/users/193133-pana
// @homepage            https://greasyfork.org/zh-CN/users/193133-pana
// @icon                data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2JhKDI5LDE2MSwyNDIsMS4wMCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYmEoMjksMTYxLDI0MiwxLjAwKSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+
// @version             6.0.5
// @description         Add notes (aliases/tags) for users to help identify and search
// @description:zh-CN   为用户添加备注(别名/标签)功能,以帮助识别和搜索
// @description:zh-TW   為使用者新增備註(別名/標籤)功能,以幫助識別和搜尋
// @license             GNU General Public License v3.0 or later
// @compatible          chrome
// @compatible          firefox
// @author              pana
// @match               *://*.instagram.com/*
// @require             https://gcore.jsdelivr.net/npm/[email protected]/minified/arrive.min.js
// @require             https://gcore.jsdelivr.net/gh/LightAPIs/greasy-fork-library@1b596ab3b97d13e3aa41dcdad1870b65944fda4d/Note_Obj.js
// @noframes
// @grant               GM_info
// @grant               GM_getValue
// @grant               GM_setValue
// @grant               GM_deleteValue
// @grant               GM_listValues
// @grant               GM_openInTab
// @grant               GM_addStyle
// @grant               GM_registerMenuCommand
// @grant               GM_unregisterMenuCommand
// @grant               GM_addValueChangeListener
// @grant               GM_removeValueChangeListener
// ==/UserScript==

(function () {
    'use strict';
    const UPDATED = '2023-02-27';
    const INS_ICON = {
        NOTE_BLACK: 'url(data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2IoMzgsIDM4LCAzOCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYigzOCwgMzgsIDM4KSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+)',
    };
    const INS_STYLE = `
    .note-obj-ins-background-box {
        display: inline-block;
        align-items: center;
        white-space: nowrap;
        border-radius: 50px;
        padding: 0px 10px;
        background-color: #336699;
        color: #fff;
    }
    .note-obj-ins-add-btn {
        background-image: ${INS_ICON.NOTE_BLACK};
        background-size: 24px;
        background-repeat: no-repeat;
        background-position: center;
        margin-left: 5px;
        cursor: pointer;
        width: 24px;
        height: 24px;
    }
    .note-obj-ins-homepage-btn {
        margin: 6px !important;
    }
    .note-obj-ins-homepage-btn:hover {
      opacity: 0.5;
    }
    .note-obj-ins-userpage-btn {
        margin-top: 2px;
    }
    .note-obj-ins-userpage-tag {
        display: block;
        font-size: 20px;
        margin-bottom: 20px;
        white-space: nowrap;
    }
    .note-obj-ins-font-bold {
        font-weight: bold;
    }
    .note-obj-veryins-blue-tag {
        background-color: #3c81df;
        color: #fff;
        display: inline-flex;
        align-items: center;
        padding: 0px 10px;
        white-space: nowrap;
        line-height: 100%;
        border-radius: 50px;
        padding: 2px 10px;
    }
    .note-obj-veryins-userpage-btn {
        display: inline-block;
        vertical-align: middle;
    }
    .note-obj-settings-frame-card label {
      color: #2f2f2f;
    }
    .note-obj-interface-dark .note-obj-settings-frame-card label {
      color: #fff;
    }
    .note-obj-group-frame-tbody input {
      color: #000;
    }`;
    const selector = {
        homepage: {
            article: '[role="main"] article',
            id: '._aaqt a',
            icon: 'span._aamz',
            commentId: '._ab8x .xt0psk2 a.notranslate',
            commentAt: '._ab8x ._aacl a.notranslate',
        },
        homepageStories: {
            id: '._aad6',
            idShell: 'li [role="menuitem"]',
        },
        homepageRecommend: {
            id: '._aak3 ._ab8x .xt0psk2 a',
        },
        userPage: {
            frame: '._aa_y',
            id: 'h2',
            bar: '.x8j4wrb',
            box: 'ul',
            common: 'span._aaai',
            suggest: '._acj1 a.notranslate',
            infoAt: '.notranslate',
            userName: '._aa_c > span',
        },
        watchList: {
            initialItem: '[role="dialog"] [aria-labelledby]',
            laterItem: '._aaei',
            id: '._ab8w a.notranslate',
        },
        stories: {
            id: 'a.notranslate',
            idShell: '._ac0o',
            cardId: '._afgd ._aacw',
        },
        dialog: {
            frame: '[role="dialog"] article',
            commentId: '._a9zc ._ab8w a, ._aacx._aacu a',
            commentAt: '._a9zs .notranslate',
        },
        request: {
            follow: '._aajc ._ab8x .xt0psk2 a',
        },
        suggest: {
            user: '._aa0- ._ab8x .xt0psk2 a',
        },
    };
    const noteObj = new Note_Obj({
        id: 'myInstagramNote',
        script: {
            author: {
                name: 'pana',
                homepage: 'https://greasyfork.org/zh-CN/users/193133-pana',
            },
            url: 'https://greasyfork.org/scripts/387871',
            updated: UPDATED,
            library: [
                {
                    name: 'arrive.js',
                    version: '2.4.1',
                    url: 'https://github.com/uzairfarooq/arrive',
                },
            ],
        },
        style: INS_STYLE,
        primaryColor: '#336699',
        settings: {
            replaceHomepageID: {
                type: 'checkbox',
                lang: {
                    en: 'Allow replacing user IDs on the home page',
                    zhHans: '允许替换首页上的用户 ID',
                    zhHant: '允許替換首頁上的使用者 ID',
                },
                default: true,
                event: instagramHomepageEvent,
            },
        },
        changeEvent: instagramChangeEvent,
    });
    function homepageNote(ele, changeId) {
        const user = Note_Obj.fn.queryAnchor(ele, selector.homepage.id);
        if (user) {
            const replaceHomepageID = noteObj.getOtherConfig().replaceHomepageID === true;
            const eleId = Note_Obj.fn.getIdFromUrl(user.href);
            if (!changeId || changeId === eleId) {
                noteObj.handler(eleId, user, undefined, {
                    add: replaceHomepageID ? undefined : 'sapn',
                    className: replaceHomepageID ? undefined : ['note-obj-ins-background-box'],
                });
            }
        }
    }
    function homepageCommentNote(ele, changeId) {
        for (const comment of Note_Obj.fn.queryAllAnchor(ele, selector.homepage.commentId, 'info')) {
            const commentId = Note_Obj.fn.getIdFromUrl(comment.href);
            if (!changeId || changeId === commentId) {
                noteObj.handler(commentId, comment);
            }
        }
    }
    function homepageCommentAtNote(ele, changeId) {
        const commentAtId = Note_Obj.fn.getIdFromUrl(ele.href);
        if (!changeId || changeId === commentAtId) {
            noteObj.handler(commentAtId, ele, undefined, {
                prefix: '@',
                title: true,
            });
        }
    }
    function dialogCommentNote(ele, chagneId) {
        const picCommentId = Note_Obj.fn.getIdFromUrl(ele.href);
        if (!chagneId || chagneId === picCommentId) {
            noteObj.handler(picCommentId, ele);
        }
    }
    function dialogCommentAtNote(ele, changeId) {
        if (!ele.classList.contains(selector.homepage.commentId.replace(/^\.|\s+.*$/g, ''))) {
            const picCommentAtId = Note_Obj.fn.getIdFromUrl(ele.href);
            if (!changeId || changeId === picCommentAtId) {
                noteObj.handler(picCommentAtId, ele, undefined, {
                    prefix: '@',
                    title: true,
                });
            }
        }
    }
    function homepageStoriesNote(ele, changeId) {
        const homepageStoriesId = Note_Obj.fn.getText(ele, selector.homepageStories.id);
        if (!changeId || changeId === homepageStoriesId) {
            ele.title = noteObj.getUserTag(homepageStoriesId);
        }
    }
    function anchorElementNote(ele, changeId) {
        const itemId = Note_Obj.fn.getIdFromUrl(ele.href);
        if (!changeId || changeId === itemId) {
            noteObj.handler(itemId, ele);
        }
    }
    function userPageNote(ele, changeId) {
        const userPageId = Note_Obj.fn.getText(ele, selector.userPage.id);
        const userPageBox = Note_Obj.fn.query(ele, selector.userPage.box);
        if (userPageId && userPageBox) {
            if (changeId) {
                if (changeId === userPageId) {
                    noteObj.handler(userPageId, ele, undefined, {
                        add: 'div',
                        after: userPageBox,
                        maskSecondaryColor: true,
                        offsetWidth: -20,
                        className: ['note-obj-ins-userpage-tag', 'note-obj-ins-font-bold'],
                    });
                }
            }
            else {
                const userNameText = Note_Obj.fn.getText(ele, selector.userPage.userName, 'warn');
                noteObj.handler(userPageId, ele, undefined, {
                    add: 'div',
                    after: userPageBox,
                    maskSecondaryColor: true,
                    offsetHeight: -20,
                    className: ['note-obj-ins-userpage-tag', 'note-obj-ins-font-bold'],
                }, userNameText);
            }
        }
    }
    function userPageCommonNote(ele, changeId) {
        for (const commonUser of Note_Obj.fn.queryAll(ele, selector.userPage.common, 'info')) {
            const commonUserId = commonUser.textContent?.trim();
            if (commonUserId) {
                if (!changeId || changeId === commonUserId)
                    noteObj.handler(commonUserId, commonUser, undefined, {
                        title: true,
                        notModify: true,
                    });
            }
        }
    }
    function userPageInfoAtNote(ele, changeId) {
        for (const infoAtUser of Note_Obj.fn.queryAllAnchor(ele, selector.userPage.infoAt, 'info')) {
            const infoAtUserId = Note_Obj.fn.getIdFromUrl(infoAtUser.href);
            if (!changeId || changeId === infoAtUserId) {
                noteObj.handler(infoAtUserId, infoAtUser, undefined, {
                    prefix: '@',
                    title: true,
                });
            }
        }
    }
    function storiesNote(ele, changeId) {
        itemNote(ele, selector.stories.id, changeId);
        Note_Obj.fn.docQueryAll(selector.stories.cardId).forEach(item => {
            const itemId = item.textContent?.trim() || '';
            if (!changeId || changeId === itemId) {
                noteObj.handler(itemId, item, undefined, {
                    notModify: true,
                    title: true,
                });
            }
        });
    }
    function watchListItemNote(ele, changeId) {
        itemNote(ele, selector.watchList.id, changeId);
    }
    function itemNote(ele, idSelector, changeId) {
        const item = Note_Obj.fn.queryAnchor(ele, idSelector);
        if (item) {
            const itemId = Note_Obj.fn.getIdFromUrl(item.href);
            if (!changeId || changeId === itemId) {
                noteObj.handler(itemId, item);
            }
        }
    }
    function instagramChangeEvent(changeId) {
        for (const article of Note_Obj.fn.docQueryAll(selector.homepage.article, 'none')) {
            homepageNote(article, changeId);
            homepageCommentNote(article, changeId);
            for (const commentAt of Note_Obj.fn.docQueryAllAnchor(selector.homepage.commentAt, 'none')) {
                homepageCommentAtNote(commentAt, changeId);
            }
            for (const picCommentUser of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentId, 'none')) {
                dialogCommentNote(picCommentUser, changeId);
            }
            for (const picCommentAt of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentAt, 'none')) {
                dialogCommentAtNote(picCommentAt, changeId);
            }
        }
        for (const homepageStories of Note_Obj.fn.docQueryAll(selector.homepageStories.idShell, 'none')) {
            homepageStoriesNote(homepageStories, changeId);
        }
        for (const homepageRecommend of Note_Obj.fn.docQueryAllAnchor(selector.homepageRecommend.id, 'none')) {
            anchorElementNote(homepageRecommend, changeId);
        }
        for (const userPage of Note_Obj.fn.docQueryAll(selector.userPage.frame, 'none')) {
            userPageNote(userPage, changeId);
            userPageCommonNote(userPage, changeId);
            userPageInfoAtNote(userPage, changeId);
        }
        for (const storiesShell of Note_Obj.fn.docQueryAll(selector.stories.idShell, 'none')) {
            storiesNote(storiesShell, changeId);
        }
        for (const initial of Note_Obj.fn.docQueryAll(selector.watchList.initialItem, 'none')) {
            watchListItemNote(initial, changeId);
        }
        for (const later of Note_Obj.fn.docQueryAll(selector.watchList.laterItem, 'none')) {
            watchListItemNote(later, changeId);
        }
        for (const dialog of Note_Obj.fn.docQueryAll(selector.dialog.frame, 'none')) {
            homepageNote(dialog, changeId);
            homepageCommentNote(dialog, changeId);
            for (const commentUser of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentId, 'none')) {
                dialogCommentNote(commentUser, changeId);
            }
            for (const commentAt of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentAt, 'none')) {
                dialogCommentAtNote(commentAt, changeId);
            }
        }
        for (const follow of Note_Obj.fn.docQueryAllAnchor(selector.request.follow, 'none')) {
            anchorElementNote(follow, changeId);
        }
        for (const suggestUser of Note_Obj.fn.docQueryAllAnchor(selector.suggest.user, 'none')) {
            anchorElementNote(suggestUser, changeId);
        }
        for (const suggest of Note_Obj.fn.docQueryAllAnchor(selector.userPage.suggest, 'none')) {
            anchorElementNote(suggest, changeId);
        }
    }
    function instagramHomepageEvent(newValue, oldValue) {
        for (const article of Note_Obj.fn.docQueryAll(selector.homepage.article)) {
            const articleUser = Note_Obj.fn.queryAnchor(article, selector.homepage.id);
            if (articleUser) {
                const articleUserId = Note_Obj.fn.getIdFromUrl(articleUser.href);
                noteObj.handler(articleUserId, articleUser, undefined, {
                    add: oldValue ? undefined : 'span',
                    className: oldValue ? undefined : ['note-obj-ins-background-box'],
                    title: oldValue,
                    restore: true,
                });
                noteObj.handler(articleUserId, articleUser, undefined, {
                    add: newValue ? undefined : 'span',
                    className: newValue ? undefined : ['note-obj-ins-background-box'],
                    title: newValue,
                });
            }
        }
    }
    function initInstagram() {
        const arriveOption = {
            fireOnAttributesModification: true,
            existing: true,
        };
        document.body.arrive(selector.homepage.article, arriveOption, article => {
            const homepageIcon = Note_Obj.fn.query(article, selector.homepage.icon);
            const articleUserId = Note_Obj.fn.getUrlId(article, selector.homepage.id);
            if (homepageIcon && articleUserId) {
                homepageIcon.insertAdjacentElement('beforebegin', noteObj.createNoteBtn(articleUserId, undefined, ['note-obj-ins-add-btn', 'note-obj-ins-homepage-btn'], 'span'));
            }
            homepageNote(article);
            homepageCommentNote(article);
            article.arrive(selector.homepage.commentAt, arriveOption, commentAt => {
                homepageCommentAtNote(commentAt);
            });
            article.arrive(selector.dialog.commentId, arriveOption, picCommentUser => {
                dialogCommentNote(picCommentUser);
            });
            article.arrive(selector.dialog.commentAt, arriveOption, picCommentAt => {
                dialogCommentAtNote(picCommentAt);
            });
        });
        document.body.arrive(selector.homepageStories.idShell, arriveOption, homepageStories => {
            homepageStoriesNote(homepageStories);
        });
        document.body.arrive(selector.homepageRecommend.id, arriveOption, homepageRecommend => {
            anchorElementNote(homepageRecommend);
        });
        document.body.arrive(selector.userPage.frame, arriveOption, userPage => {
            const userPageBar = Note_Obj.fn.query(userPage, selector.userPage.bar);
            const userPageId = Note_Obj.fn.getText(userPage, selector.userPage.id);
            if (userPageBar && userPageId) {
                const userNameText = Note_Obj.fn.getText(userPage, selector.userPage.userName, 'info');
                userPageBar.after(noteObj.createNoteBtn(userPageId, userNameText, ['note-obj-ins-add-btn', 'note-obj-ins-userpage-btn']));
            }
            userPageNote(userPage);
            userPageCommonNote(userPage);
            userPageInfoAtNote(userPage);
        });
        document.body.arrive(selector.stories.idShell, arriveOption, storiesShell => {
            storiesNote(storiesShell);
            const stories = Note_Obj.fn.queryAnchor(storiesShell, selector.stories.id);
            if (stories) {
                const userIdChange = new MutationObserver(() => {
                    const newUserId = Note_Obj.fn.getIdFromUrl(stories.href);
                    if (noteObj.judgeUsers(newUserId)) {
                        noteObj.handler(newUserId, stories);
                    }
                    else {
                        noteObj.handler(newUserId, stories, undefined, {
                            restore: true,
                        });
                    }
                });
                userIdChange.observe(stories, {
                    attributeFilter: ['href'],
                });
            }
        });
        document.body.arrive(selector.watchList.initialItem, arriveOption, initial => {
            watchListItemNote(initial);
        });
        document.body.arrive(selector.watchList.laterItem, arriveOption, later => {
            watchListItemNote(later);
        });
        document.body.arrive(selector.dialog.frame, arriveOption, dialog => {
            const homepageIcon = Note_Obj.fn.query(dialog, selector.homepage.icon);
            const dialogUserId = Note_Obj.fn.getUrlId(dialog, selector.homepage.id);
            if (homepageIcon && dialogUserId) {
                homepageIcon.insertAdjacentElement('beforebegin', noteObj.createNoteBtn(dialogUserId, undefined, ['note-obj-ins-add-btn', 'note-obj-ins-homepage-btn'], 'span'));
            }
            homepageNote(dialog);
            homepageCommentNote(dialog);
            dialog.arrive(selector.dialog.commentId, arriveOption, commentUser => {
                dialogCommentNote(commentUser);
            });
            dialog.arrive(selector.dialog.commentAt, arriveOption, commentAt => {
                dialogCommentAtNote(commentAt);
            });
        });
        document.body.arrive(selector.request.follow, arriveOption, follow => {
            anchorElementNote(follow);
        });
        document.body.arrive(selector.suggest.user, arriveOption, suggestUser => {
            anchorElementNote(suggestUser);
        });
        document.body.arrive(selector.userPage.suggest, arriveOption, suggest => {
            anchorElementNote(suggest);
        });
    }
    initInstagram();
})();