Telegram Web Sort Panel + Chart + Lang (FIXED)

Fixed panel rendering & interaction for Telegram Web

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Telegram Web Sort Panel + Chart + Lang (FIXED)
// @namespace    https://example.com
// @version      3.3.1
// @description  Fixed panel rendering & interaction for Telegram Web
// @match        https://web.telegram.org/a/*
// @match        https://web.telegram.org/k/*
// @grant        GM_addStyle
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  /* ===== wait for Telegram UI ===== */
  function waitForUI(cb) {
    const i = setInterval(() => {
      if (document.querySelector('#column-center, .chat')) {
        clearInterval(i);
        cb();
      }
    }, 500);
  }

  waitForUI(init);

  function init() {

    /* ===== load Chart.js safely ===== */
    if (!window.Chart) {
      const s = document.createElement('script');
      s.src = 'https://cdn.jsdelivr.net/npm/chart.js';
      s.onload = createPanel;
      document.head.appendChild(s);
    } else {
      createPanel();
    }

    function createPanel() {

      GM_addStyle(`
        #ua-panel {
          position: fixed;
          top: 120px;
          right: 20px;
          width: 300px;
          background: var(--tg-theme-bg-color, #fff);
          color: var(--tg-theme-text-color, #000);
          border-radius: 10px;
          box-shadow: 0 4px 20px rgba(0,0,0,.4);
          padding: 8px;
          z-index: 2147483647; /* FIX */
          font-family: Arial,sans-serif;
        }
        #ua-panel-header {
          display:flex;
          justify-content:space-between;
          cursor:move;
          font-weight:bold;
        }
        #ua-buttons button, #ua-lang select {
          margin:2px;
          padding:4px 6px;
          border:none;
          border-radius:4px;
          background:#0d6efd;
          color:#fff;
          font-size:12px;
          cursor:pointer;
        }
        #ua-chart {height:200px;}
      `);

      if (document.getElementById('ua-panel')) return;

      const panel = document.createElement('div');
      panel.id = 'ua-panel';
      panel.innerHTML = `
        <div id="ua-panel-header">
          <span>🇺🇦 Telegram Panel</span>
          <span id="ua-hide">✖</span>
        </div>
        <div id="ua-buttons">
          <button data-sort="reactions">Reactions</button>
          <button data-sort="date_desc">Date ↓</button>
          <button data-sort="date_asc">Date ↑</button>
          <button data-sort="media">Media</button>
          <button data-sort="length">Length</button>
          <button data-action="refresh">Refresh</button>
        </div>
        <canvas id="ua-chart"></canvas>
      `;
      document.body.appendChild(panel);

      panel.querySelector('#ua-hide').onclick = () => panel.remove();

      /* ===== chart ===== */
      const ctx = panel.querySelector('#ua-chart').getContext('2d');
      const chart = new Chart(ctx, {
        type: 'bar',
        data: {
          labels: [],
          datasets: [{ data: [], backgroundColor: [] }]
        },
        options: { responsive: true }
      });

      /* ===== message collector (FIXED) ===== */
      function collect() {
        return [...document.querySelectorAll(
          '.message, .Message, [data-message-id]'
        )].map(m => ({
          text: m.innerText || '',
          reactions: m.querySelectorAll('[class*="reaction"]').length,
          media: m.querySelectorAll('img,video').length
        }));
      }

      function update(type) {
        let data = collect().map(m => ({
          value:
            type === 'reactions' ? m.reactions :
            type === 'media' ? m.media :
            m.text.length
        })).sort((a,b)=>b.value-a.value);

        chart.data.labels = data.slice(0,10).map((_,i)=>i+1);
        chart.data.datasets[0].data = data.slice(0,10).map(d=>d.value);
        chart.data.datasets[0].backgroundColor =
          data.slice(0,10).map(()=>`hsl(${Math.random()*360},70%,50%)`);
        chart.update();
      }

      panel.querySelectorAll('button').forEach(b=>{
        b.onclick=()=>update(b.dataset.sort||'reactions');
      });

      setTimeout(()=>update('reactions'),2000);
    }
  }
})();