<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Giuseppe Carlà</title>
    <description>The latest articles on DEV Community by Giuseppe Carlà (@scibilo).</description>
    <link>https://dev.to/scibilo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3856138%2F9ae1dbfb-a981-467c-b4cc-54ab8107e4de.jpg</url>
      <title>DEV Community: Giuseppe Carlà</title>
      <link>https://dev.to/scibilo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scibilo"/>
    <language>en</language>
    <item>
      <title>Telepage – I built a self-hosted PHP app that turns any Telegram channel into a website</title>
      <dc:creator>Giuseppe Carlà</dc:creator>
      <pubDate>Wed, 01 Apr 2026 17:55:48 +0000</pubDate>
      <link>https://dev.to/scibilo/telepage-i-built-a-self-hosted-php-app-that-turns-any-telegram-channel-into-a-website-2i2b</link>
      <guid>https://dev.to/scibilo/telepage-i-built-a-self-hosted-php-app-that-turns-any-telegram-channel-into-a-website-2i2b</guid>
      <description>&lt;p&gt;If you run a Telegram channel, you already know the problem: your content is invisible to Google, there's no search, old posts are buried, and readers need the app just to see your work.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Telepage&lt;/strong&gt; to fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Telepage connects to your Telegram channel via a bot webhook and turns every post into a searchable web card — automatically, in real time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0zcxkaob0c3c5sglju1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0zcxkaob0c3c5sglju1.png" alt="Recipes site with 952 posts, colored tags and AI summaries" width="800" height="1309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every hashtag in your Telegram posts becomes a colored navigation filter. Every link gets its Open Graph metadata scraped. Every post gets an AI-generated summary and tags if you connect a Gemini key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6d7bufxigjavv8og5swp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6d7bufxigjavv8og5swp.png" alt="Science/news channel" width="800" height="1076"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The tech stack
&lt;/h2&gt;

&lt;p&gt;Pure PHP 8.1, SQLite with WAL mode, vanilla JS. No frameworks, no Composer, no build step, no MySQL. It runs on standard shared hosting — I tested it on Aruba (a very restrictive Italian host).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Telegram channel
      │
      ▼ webhook (instant)
PHP 8.1 + SQLite
      │
      ▼
Your website — card grid, search, tag filters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interesting technical decisions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Session isolation per installation&lt;/strong&gt;&lt;br&gt;
Multiple Telepage sites on the same domain (e.g. &lt;code&gt;site.com/news/&lt;/code&gt; and &lt;code&gt;site.com/recipes/&lt;/code&gt;) need completely separate admin sessions. I solved this with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tp_'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TELEPAGE_ROOT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nb"&gt;session_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each installation path produces a unique session name — no shared cookies, no cross-login.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;History Scanner&lt;/strong&gt;&lt;br&gt;
Telegram's Bot API has no "get all past messages" endpoint. To import historical content I use the &lt;code&gt;forwardMessage&lt;/code&gt; trick: forward each message ID from the channel to itself, read the content, then immediately delete the forwarded copy. It scans backwards from the most recent ID, skipping gaps from deleted messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI integration&lt;/strong&gt;&lt;br&gt;
Optional Google Gemini integration auto-tags and summarizes every post. The models available via the free tier change frequently — I built a cascade fallback that tries multiple model names in order and logs exactly which one succeeded.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like in production
&lt;/h2&gt;

&lt;p&gt;I've been running it on two test channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A science/news channel: 23 posts, tagged by topic&lt;/li&gt;
&lt;li&gt;A recipes channel: 952 posts, fully tagged and summarized by AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The recipes site went from zero to 952 searchable, tagged posts in a few hours using the History Scanner.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm less happy with
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI calls are currently synchronous in the admin panel — for large archives you click "Process AI" repeatedly. A proper background queue would be better.&lt;/li&gt;
&lt;li&gt;The History Scanner requires manual ID tuning when posts are missing — not ideal for non-technical users.&lt;/li&gt;
&lt;li&gt;No pagination on the install wizard, though the 5-step flow works fine in practice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/scibilo/telepage" rel="noopener noreferrer"&gt;github.com/scibilo/telepage&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's MIT licensed. Works on any PHP 8.1+ shared hosting with HTTPS. The install wizard takes about 5 minutes.&lt;/p&gt;

&lt;p&gt;Feedback welcome — this is the first public release and I'm actively improving it.&lt;/p&gt;

</description>
      <category>php</category>
      <category>opensource</category>
      <category>telegram</category>
      <category>selfhosted</category>
    </item>
  </channel>
</rss>
