67

Добавил иконки в Gmail почту! Красота =)1

Сегодня переписал бесплатное расширение меток для гугловской почты и теперь это расширение, которое добавляет в гугл почту иконки отправителей, а если иконки нет, то показывается метку.

Мне кажется, что стало красиво!
Скачать можно тут https://chromewebstore.google.com/detail/gmail-email-icons/n...

Стало:

Было:

Работает локально, ничего не собирает, никуда не отправляет, рекламу не показывает.
Всё как мы любим.

Поднимите вверх, чтоб больше людей увидело =)

Правила сообщества

-Ставьте наши теги, если Ваш пост о программе, приложении или браузере(в том числе о расширениях, дополнениях в нему), его недоработке, баге, обновлении. Это может быть пост - обзор или отзыв.

-При возникновении споров относитесь с уважением друг к другу, а так же приводите аргументы.

Разрешено всё, что не запрещено правилами Пикабу.

8
Автор поста оценил этот комментарий
ответный пост

Напомню. Я сделал расширение, которое добавляет иконки в Gmail почту. И в комментариях попросили сделать не расширением, а скриптом для Tampermonkey. Мне было лень, но потом я подумал почему бы и нет. Скинул код в chatGPT и он подогнал его под формат Tampermonkey. Ниже расскажу как поставить чтоб работало.

Выглядит вот так:

Продолжение поста «Добавил иконки в Gmail почту! Красота =)»

Как поставить?

  1. Установить Tampermonkey если у вас его ещё нет https://chromewebstore.google.com/detail/tampermonkey/dhdgff...

  2. Правой кнопкой по иконке расширения и Параметры

  3. В верхнем меню панели Tampermonkey жмём на плюсик и открывается страница создания нового скрипта

  4. Вставляем код скприта сюда и сохраняем

  5. Готово.

Вот сам код скрипта:

// ==UserScript==

// @name Gmail Sender Favicon (base + S2, fallback letter)

// @namespace https://tampermonkey.net/

// @VERSION 2026-01-02

// @Description Gmail: show sender base-domain favicon; fallback to Google S2; then fallback to letter.

// @match https://mail.google.com/*

// @run-at document-idle

// ==/UserScript==

(() => {

'use strict';

const STYLE_ID = 'gm-sender-favicon-style-final';

const BADGE_CLASS = 'gm-sender-badge-final';

function injectStyles() {

if (document.getElementById(STYLE_ID)) return;

const style = document.createElement('style');

style.id = STYLE_ID;

style.textContent = `

.${BADGE_CLASS}{

display:inline-flex;

align-items:center;

margin-right:6px;

vertical-align:middle;

flex: 0 0 auto;

transform: translateY(-0.5px);

}

.${BADGE_CLASS} img{

width:14px;height:14px;border-radius:3px;display:block;

}

.${BADGE_CLASS} .gm-letter{

font-weight:700;

font-size:12px;

line-height:1;

display:inline-block;

}

`;

document.documentElement.appendChild(style);

}

// ---------- domain/email helpers ----------

function extractEmailLike(str) {

if (!str) return '';

const s = String(str);

// <mail@domain>

const m1 = s.match(/<\s*([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,})\s*>/i);

if (m1) return m1[1];

// mail@domain

const m2 = s.match(/([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,})/i);

if (m2) return m2[1];

return '';

}

function getDomainFromEmail(email) {

const at = (email || '').indexOf('@');

if (at === -1) return '';

return email.slice(at + 1).trim().toLowerCase();

}

// Простая нормализация к base-domain: example.com

function baseDomain(domain) {

const parts = (domain || '').split('.').filter(Boolean);

if (parts.length < 2) return domain || '';

return parts.slice(-2).join('.');

}

// стабильный цвет по seed (чтобы буква была “привычного” цвета)

function stableColor(seed) {

let h = 0;

for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) | 0;

const hue = Math.abs(h) % 360;

return `hsl(${hue}, 70%, 40%)`;

}

function pickLetter(senderText, base) {

const m = (senderText || '').match(/[a-z0-9а-яё]/i);

if (m) return m[0].toUpperCase();

if (base) return base[0].toUpperCase();

return '?';

}

// ---------- locating sender in a row ----------

function findSenderInfo(row) {

// 1) самый надежный: span[email]

const emailSpan = row.querySelector('span[email]');

if (emailSpan) {

const email = emailSpan.getAttribute('email') || '';

const senderText =

(emailSpan.getAttribute('name') || '').trim() ||

(emailSpan.getAttribute('aria-label') || '').trim() ||

(emailSpan.textContent || '').trim();

return { email, senderText, senderNode: emailSpan };

}

// 2) data-hovercard-id часто содержит email

const hcSpan = row.querySelector('span[data-hovercard-id]');

if (hcSpan) {

const hc = hcSpan.getAttribute('data-hovercard-id') || '';

const email = extractEmailLike(hc) || extractEmailLike(hcSpan.getAttribute('aria-label')) || '';

const senderText =

(hcSpan.getAttribute('name') || '').trim() ||

(hcSpan.getAttribute('aria-label') || '').trim() ||

(hcSpan.textContent || '').trim();

return { email, senderText, senderNode: hcSpan };

}

// 3) иногда sender виден как yP/zF

const nameSpan = row.querySelector('span.yP, span.zF');

if (nameSpan) {

const email =

extractEmailLike(nameSpan.getAttribute('aria-label')) ||

extractEmailLike(nameSpan.getAttribute('title')) ||

'';

const senderText =

(nameSpan.getAttribute('aria-label') || '').trim() ||

(nameSpan.getAttribute('title') || '').trim() ||

(nameSpan.textContent || '').trim();

return { email, senderText, senderNode: nameSpan };

}

return null;

}

// ---------- badge creation ----------

function makeBadge(base, letter, fontSizePx) {

const badge = document.createElement('span');

badge.className = BADGE_CLASS;

badge.setAttribute('data-base', base || '');

const img = document.createElement('img');

img.alt = '';

img.referrerPolicy = 'no-referrer';

const sources = base

? [

`https://${base}/favicon.ico`,

`https://www.google.com/s2/favicons?sz=64&amp;domain=%24%7Ben...}`

]

: [];

let idx = 0;

const fallbackToLetter = () => {

img.remove();

const span = document.createElement('span');

span.className = 'gm-letter';

span.textContent = letter;

span.style.color = stableColor(base || letter || 'x');

if (fontSizePx) span.style.fontSize = fontSizePx;

badge.appendChild(span);

};

img.onerror = () => {

idx++;

if (idx < sources.length) {

img.src = sources[idx];

} else {

fallbackToLetter();

}

};

if (sources.length) {

img.src = sources[0];

badge.appendChild(img);

} else {

fallbackToLetter();

}

return badge;

}

// ---------- main enhancer ----------

function enhanceRow(row) {

const info = findSenderInfo(row);

if (!info) return;

const domain = getDomainFromEmail(info.email);

const base = baseDomain(domain);

// текст для буквы

const senderText = info.senderText || (info.senderNode?.textContent || '').trim();

const letter = pickLetter(senderText, base);

// куда вставлять:

// A) предпочитаем контейнер отправителя (как у тебя работало)

let target = row.querySelector('.yX.xY');

// B) если нет — рядом с узлом senderNode

let insertBeforeNode = null;

if (!target && info.senderNode && info.senderNode.parentElement) {

target = info.senderNode.parentElement;

insertBeforeNode = info.senderNode;

}

if (!target) return;

// уже вставлено?

const existing = target.querySelector(`:scope > .${BADGE_CLASS}`);

if (existing) {

const exBase = existing.getAttribute('data-base') || '';

if (exBase === (base || '')) return;

existing.remove();

}

const cs = window.getComputedStyle(target);

const fontSizePx = cs.fontSize ? `calc(${cs.fontSize} - 1px)` : '';

const badge = makeBadge(base, letter, fontSizePx);

if (insertBeforeNode) target.insertBefore(badge, insertBeforeNode);

else target.prepend(badge);

}

function enhanceAll() {

injectStyles();

document.querySelectorAll('.zA').forEach(enhanceRow);

}

// ---------- observer (throttled) ----------

let rafPending = false;

function schedule() {

if (rafPending) return;

rafPending = true;

requestAnimationFrame(() => {

rafPending = false;

enhanceAll();

});

}

const obs = new MutationObserver(schedule);

obs.observe(document.body, { childList: true, subtree: true });

enhanceAll();

})();

Показать полностью
комментарии (3)
1
Автор поста оценил этот комментарий
Скоро опять все на почту перейдем для общения.
раскрыть ветку (1)
2
Автор поста оценил этот комментарий

И вход на почту будет только через Макс )

11
Автор поста оценил этот комментарий

Идея неплохая, но почта – слишком чувствительная штука, чтобы давать доступ к странице расширению малоизвестного разработчика (проверять код при каждом апдейте и анализировать на уязвимости я не готов). Надеюсь, гугл её спиздит :-)

раскрыть ветку (1)
3
Автор поста оценил этот комментарий

Понимаю параною. И даже одобряю )

2
Автор поста оценил этот комментарий

Ребята, а минусы за что?

показать ответы
4
потрачено за 500
Автор поста оценил этот комментарий

тут мысль в чем:

- ты цель, имеешь расширение. я это, доспустим, знаю.
- я со своего специального (подконтрольного) домена присылаю тебе письмо.
- ты его получаешь. расширение твое безвродное, да. но оно из твоего браузера лезет на мой домен, получает фавиконку. у тебя все красиво.

- а я вижу твой ип адрес в логах. при неком стечении обьстоятельств можно сопоставить отправку письма и примерное время его прочтения.

вот о таком векторе атаки, хоть и с начтяжкой, @BustedChandelier говорил.


это как раньше в пистьме делали скрытую метку о прочтении - вставляли html разметку с 1-пиксельной прозрачной гифкой или картинкой. письмо открыл, оно подгрузилась, в логах подгрузился ты. тут аналогично, но уже автоматизированно расширением.


это не хорошо и не плохо. тут каждый выбирает сам. у некоторых в настройках вообще запрещена подгрузка любых картинок.

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

а, да, о такой вариант мне в голову не пришёл. Теперь понял )

4
ВЗДРЪЖНИ ЭФФЕКТ
Автор поста оценил этот комментарий

Интересно выходит: отправляешь юзеру письмишко со специального домена, его браузер потом идёт за favicon-ом к тебе, и вуаля - "я тебя по ip вычислю". Молодец, умно придумал, я сразу и не догадался!

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

так это...
вот получил я письмо с от pisaka @ BustedChandelier . ru и расширение пойдёт искать иконку этого домена. Но и без рсширения глазками видно ведь домен в адресе почты ))

показать ответы
Автор поста оценил этот комментарий

вы забыли про "похуЮ"...

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

да про это уже никто не помнит и мы всё дальше от бога

0
Галактико опасносте!
Автор поста оценил этот комментарий
А может это лучше сделать не как расширение для целого хрома, в как плагин к почте? Такая возможность у Gmail то есть
раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Может и можно, о но я не умею плагины эти делать и сделал так как умею )

1
Автор поста оценил этот комментарий

Помню, лет 25 назад я такой же хуйнёй страдал. С аутлуком.

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

А теперь как веселитесь?)

показать ответы
0
Автор поста оценил этот комментарий

А сколько пд оно пиздит?

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Гугл говорит, что файлы расширения лежат где-то тут

Иллюстрация к комментарию
3
Безлимит эмо какашек
Автор поста оценил этот комментарий

Спасибо

Нравится

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Пожалуйста)

0
DELETED
Автор поста оценил этот комментарий
Как дополнение для tampermonkey было бы идеально
раскрыть ветку (1)
0
Автор поста оценил этот комментарий

только для тех кто им пользуется )
но вообще да, чтоб не плодить сущности было бы здорово, чтоб кто-то такое сделал. Потому что я темперманки я тоже использую

0
Автор поста оценил этот комментарий
если что-то бесплатно то продукт это ты
раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Нет, ты 😂

4
Автор поста оценил этот комментарий

спасибо удобно

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Пожалуйста 8)

0
Автор поста оценил этот комментарий

А сколько пд оно пиздит?

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Ни сколько )
Ты можешь найти папку расширения и посмотреть его код. Он не большой, так что разобраться будет просто.
Расширение вообще ничего ни куда не отправляет и не собирает никаких данных. Отдел маркетинга Яндекса наверное бы умер от такого, а я вот сделал.

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества