// ==UserScript==
// @name Instagram - Add notes to the user
// @name:en Instagram - Add notes to the user
// @name:zh-CN Instagram - 为用户添加备注(别名/标签)
// @name:zh-TW Instagram - 為使用者新增備註(別名/標籤)
// @namespace https://gf.qytechs.cn/zh-CN/users/193133-pana
// @homepage https://gf.qytechs.cn/zh-CN/users/193133-pana
// @icon data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2JhKDI5LDE2MSwyNDIsMS4wMCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYmEoMjksMTYxLDI0MiwxLjAwKSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+
// @version 6.0.1
// @description Add notes (aliases/tags) for users to help identify and search
// @description:en 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@fd5c9360dd6db517c47e8e1dd25f3ff92a537938/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-26';
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://gf.qytechs.cn/zh-CN/users/193133-pana',
},
url: 'https://gf.qytechs.cn/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, userId) {
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 ((userId && userId === eleId) || (!userId && noteObj.judgeUsers(eleId))) {
noteObj.handler(eleId, user, undefined, {
add: replaceHomepageID ? undefined : 'sapn',
className: replaceHomepageID ? undefined : ['note-obj-ins-background-box'],
});
}
}
}
function homepageCommentNote(ele, userId) {
for (const comment of Note_Obj.fn.queryAllAnchor(ele, selector.homepage.commentId, 'info')) {
const commentId = Note_Obj.fn.getIdFromUrl(comment.href);
if ((userId && userId === commentId) || (!userId && noteObj.judgeUsers(commentId))) {
noteObj.handler(commentId, comment, undefined, {});
}
}
}
function homepageCommentAtNote(ele, userId) {
const commentAtId = Note_Obj.fn.getIdFromUrl(ele.href);
if ((userId && userId === commentAtId) || (!userId && noteObj.judgeUsers(commentAtId))) {
noteObj.handler(commentAtId, ele, undefined, {
prefix: '@',
title: true,
});
}
}
function dialogCommentNote(ele, userId) {
const picCommentId = Note_Obj.fn.getIdFromUrl(ele.href);
if ((userId && userId === picCommentId) || (!userId && noteObj.judgeUsers(picCommentId))) {
noteObj.handler(picCommentId, ele);
}
}
function dialogCommentAtNote(ele, userId) {
if (!ele.classList.contains(selector.homepage.commentId.replace(/^\.|\s+.*$/g, ''))) {
const picCommentAtId = Note_Obj.fn.getIdFromUrl(ele.href);
if ((userId && userId === picCommentAtId) || (!userId && noteObj.judgeUsers(picCommentAtId))) {
noteObj.handler(picCommentAtId, ele, undefined, {
prefix: '@',
title: true,
});
}
}
}
function homepageStoriesNote(ele, userId) {
const homepageStoriesId = Note_Obj.fn.getText(ele, selector.homepageStories.id);
if ((userId && userId === homepageStoriesId) || (!userId && noteObj.judgeUsers(homepageStoriesId))) {
ele.title = noteObj.getUserTag(homepageStoriesId);
}
}
function anchorElementNote(ele, userId) {
const itemId = Note_Obj.fn.getIdFromUrl(ele.href);
if ((userId && userId === itemId) || (!userId && noteObj.judgeUsers(itemId))) {
noteObj.handler(itemId, ele);
}
}
function userPageNote(ele, userId) {
const userPageId = Note_Obj.fn.getText(ele, selector.userPage.id);
const userPageBox = Note_Obj.fn.query(ele, selector.userPage.box);
if (userPageId && userPageBox) {
if (userId) {
if (userId === 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, userId) {
for (const commonUser of Note_Obj.fn.queryAll(ele, selector.userPage.common, 'info')) {
const commonUserId = commonUser.textContent?.trim();
if (commonUserId) {
if ((userId && userId === commonUserId) || (!userId && noteObj.judgeUsers(commonUserId)))
noteObj.handler(commonUserId, commonUser, undefined, {
title: true,
notModify: true,
});
}
}
}
function userPageInfoAtNote(ele, userId) {
for (const infoAtUser of Note_Obj.fn.queryAllAnchor(ele, selector.userPage.infoAt, 'info')) {
const infoAtUserId = Note_Obj.fn.getIdFromUrl(infoAtUser.href);
if ((userId && userId === infoAtUserId) || (!userId && noteObj.judgeUsers(infoAtUserId))) {
noteObj.handler(infoAtUserId, infoAtUser, undefined, {
prefix: '@',
title: true,
});
}
}
}
function storiesNote(ele, userId) {
itemNote(ele, selector.stories.id, userId);
Note_Obj.fn.docQueryAll(selector.stories.cardId).forEach(item => {
const itemId = item.textContent?.trim() || '';
if ((userId && userId === itemId) || (!userId && noteObj.judgeUsers(itemId))) {
noteObj.handler(itemId, item, undefined, {
notModify: true,
title: true,
});
}
});
}
function watchListItemNote(ele, userId) {
itemNote(ele, selector.watchList.id, userId);
}
function itemNote(ele, idSelector, userId) {
const item = Note_Obj.fn.queryAnchor(ele, idSelector);
if (item) {
const itemId = Note_Obj.fn.getIdFromUrl(item.href);
if ((userId && userId === itemId) || (!userId && noteObj.judgeUsers(itemId))) {
noteObj.handler(itemId, item);
}
}
}
function instagramChangeEvent(userId) {
for (const article of Note_Obj.fn.docQueryAll(selector.homepage.article, 'none')) {
homepageNote(article, userId);
homepageCommentNote(article, userId);
for (const commentAt of Note_Obj.fn.docQueryAllAnchor(selector.homepage.commentAt, 'none')) {
homepageCommentAtNote(commentAt, userId);
}
for (const picCommentUser of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentId, 'none')) {
dialogCommentNote(picCommentUser, userId);
}
for (const picCommentAt of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentAt, 'none')) {
dialogCommentAtNote(picCommentAt, userId);
}
}
for (const homepageStories of Note_Obj.fn.docQueryAll(selector.homepageStories.idShell, 'none')) {
homepageStoriesNote(homepageStories, userId);
}
for (const homepageRecommend of Note_Obj.fn.docQueryAllAnchor(selector.homepageRecommend.id, 'none')) {
anchorElementNote(homepageRecommend, userId);
}
for (const userPage of Note_Obj.fn.docQueryAll(selector.userPage.frame, 'none')) {
userPageNote(userPage, userId);
userPageCommonNote(userPage, userId);
userPageInfoAtNote(userPage, userId);
}
for (const storiesShell of Note_Obj.fn.docQueryAll(selector.stories.idShell, 'none')) {
storiesNote(storiesShell, userId);
}
for (const initial of Note_Obj.fn.docQueryAll(selector.watchList.initialItem, 'none')) {
watchListItemNote(initial, userId);
}
for (const later of Note_Obj.fn.docQueryAll(selector.watchList.laterItem, 'none')) {
watchListItemNote(later, userId);
}
for (const dialog of Note_Obj.fn.docQueryAll(selector.dialog.frame, 'none')) {
homepageNote(dialog, userId);
homepageCommentNote(dialog, userId);
for (const commentUser of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentId, 'none')) {
dialogCommentNote(commentUser, userId);
}
for (const commentAt of Note_Obj.fn.docQueryAllAnchor(selector.dialog.commentAt, 'none')) {
dialogCommentAtNote(commentAt, userId);
}
}
for (const follow of Note_Obj.fn.docQueryAllAnchor(selector.request.follow, 'none')) {
anchorElementNote(follow, userId);
}
for (const suggestUser of Note_Obj.fn.docQueryAllAnchor(selector.suggest.user, 'none')) {
anchorElementNote(suggestUser, userId);
}
for (const suggest of Note_Obj.fn.docQueryAllAnchor(selector.userPage.suggest, 'none')) {
anchorElementNote(suggest, userId);
}
}
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();
})();