diff --git a/src/channelsyncroniser.ts b/src/channelsyncroniser.ts
index 9f24d6f1d56fad4c5990ef3c7b1fcf53e417af83..8c3a434beb14a63614a35c6cbcdaa26f458d184f 100644
--- a/src/channelsyncroniser.ts
+++ b/src/channelsyncroniser.ts
@@ -164,6 +164,28 @@ export class ChannelSyncroniser {
         return rooms.map((room) => room.matrix!.getId() as string);
     }
 
+    public async GetAliasFromChannel(channel: Discord.Channel): Promise<string | null> {
+        let rooms: string[] = [];
+        try {
+            rooms = await this.GetRoomIdsFromChannel(channel);
+        } catch (err) { } // do nothing, our rooms array will just be empty
+        for (const room of rooms) {
+            try {
+                const al = (await this.bridge.getIntent().getClient()
+                    .getStateEvent(room, "m.room.canonical_alias")).alias;
+                if (al) {
+                    return al; // we are done, we found an alias
+                }
+            } catch (err) { } // do nothing, as if we error we just roll over to the next entry
+        }
+        const guildChannel = channel as Discord.TextChannel;
+        if (!guildChannel.guild) {
+            return null; // we didn't pass a guild, so we have no way of bridging this room, thus no alias
+        }
+        // at last, no known canonical aliases and we are ag uild....so we know an alias!
+        return `#_discord_${guildChannel.guild.id}_${channel.id}:${this.config.bridge.domain}`;
+    }
+
     public async GetChannelUpdateState(channel: Discord.TextChannel, forceUpdate = false): Promise<IChannelState> {
         log.verbose(`State update request for ${channel.id}`);
         const channelState: IChannelState = Object.assign({}, DEFAULT_CHANNEL_STATE, {
diff --git a/src/discordmessageprocessor.ts b/src/discordmessageprocessor.ts
index d1328fe7c5aa6e11ee5fefa35d444bf444b63fc3..8e9dd555e7004de1bc79192ebdd165b0ef094573 100644
--- a/src/discordmessageprocessor.ts
+++ b/src/discordmessageprocessor.ts
@@ -19,18 +19,25 @@ import * as markdown from "discord-markdown";
 import { DiscordBot } from "./bot";
 import * as escapeHtml from "escape-html";
 import { Util } from "./util";
+import { Bridge } from "matrix-appservice-bridge";
 
 import { Log } from "./log";
 const log = new Log("DiscordMessageProcessor");
 
 const MATRIX_TO_LINK = "https://matrix.to/#/";
-const MXC_INSERT_REGEX = /\x01(\w+)\x01([01])\x01([0-9]*)\x01/g;
+// somehow the regex works properly if it isn't global
+// as we replace the match fully anyways this shouldn't be an issue
+const MXC_INSERT_REGEX = /\x01emoji\x01(\w+)\x01([01])\x01([0-9]*)\x01/;
 const NAME_MXC_INSERT_REGEX_GROUP = 1;
 const ANIMATED_MXC_INSERT_REGEX_GROUP = 2;
 const ID_MXC_INSERT_REGEX_GROUP = 3;
 const EMOJI_SIZE = 32;
 const MAX_EDIT_MSG_LENGTH = 50;
 
+// same as above, no global flag here, too
+const CHANNEL_INSERT_REGEX = /\x01chan\x01([0-9]*)\x01/;
+const ID_CHANNEL_INSERT_REGEX = 1;
+
 export class DiscordMessageProcessorOpts {
     constructor(readonly domain: string, readonly bot?: DiscordBot) {
 
@@ -82,10 +89,12 @@ export class DiscordMessageProcessor {
         });
         content = this.InsertEmbeds(content, msg);
         content = await this.InsertMxcImages(content, msg);
+        content = await this.InsertChannelPills(content, msg);
 
         // parse postmark stuff
         contentPostmark = this.InsertEmbedsPostmark(contentPostmark, msg);
         contentPostmark = await this.InsertMxcImages(contentPostmark, msg, true);
+        contentPostmark = await this.InsertChannelPills(contentPostmark, msg, true);
 
         result.body = content;
         result.formattedBody = contentPostmark;
@@ -221,18 +230,11 @@ export class DiscordMessageProcessor {
         return `<a href="${MATRIX_TO_LINK}${escapeHtml(memberId)}">${escapeHtml(memberName)}</a>`;
     }
 
-    public InsertChannel(node: IDiscordNode, msg: Discord.Message, html: boolean = false): string {
-        const id = node.id;
-        const channel = msg.guild.channels.get(id);
-        if (!channel) {
-            return html ? `&lt;#${escapeHtml(id)}&gt;` : `<#${id}>`;
-        }
-        const channelStr = escapeHtml(channel ? "#" + channel.name : "#" + id);
-        if (!html) {
-            return channelStr;
-        }
-        const roomId = escapeHtml(`#_discord_${msg.guild.id}_${id}:${this.opts.domain}`);
-        return `<a href="${MATRIX_TO_LINK}${roomId}">${escapeHtml(channelStr)}</a>`;
+    public InsertChannel(node: IDiscordNode): string {
+        // unfortunately these callbacks are sync, so we flag our channel with some special stuff
+        // and later on grab the real channel pill async
+        const FLAG = "\x01";
+        return `${FLAG}chan${FLAG}${node.id}${FLAG}`;
     }
 
     public InsertRole(node: IDiscordNode, msg: Discord.Message, html: boolean = false): string {
@@ -252,8 +254,7 @@ export class DiscordMessageProcessor {
         // unfortunately these callbacks are sync, so we flag our url with some special stuff
         // and later on grab the real url async
         const FLAG = "\x01";
-        const name = escapeHtml(node.name);
-        return `${FLAG}${name}${FLAG}${node.animated ? 1 : 0}${FLAG}${node.id}${FLAG}`;
+        return `${FLAG}emoji${FLAG}${node.name}${FLAG}${node.animated ? 1 : 0}${FLAG}${node.id}${FLAG}`;
     }
 
     public InsertRoom(msg: Discord.Message, def: string): string {
@@ -267,10 +268,12 @@ export class DiscordMessageProcessor {
             const animated = results[ANIMATED_MXC_INSERT_REGEX_GROUP] === "1";
             const id = results[ID_MXC_INSERT_REGEX_GROUP];
             let replace = "";
+            const nameHtml = escapeHtml(name);
             try {
                 const mxcUrl = await this.opts.bot!.GetEmoji(name, animated, id);
                 if (html) {
-                    replace = `<img alt="${name}" title="${name}" height="${EMOJI_SIZE}" src="${mxcUrl}" />`;
+                    replace = `<img alt="${nameHtml}" title="${nameHtml}" ` +
+                        `height="${EMOJI_SIZE}" src="${mxcUrl}" />`;
                 } else {
                     replace = `:${name}:`;
                 }
@@ -279,18 +282,39 @@ export class DiscordMessageProcessor {
                     `Could not insert emoji ${id} for msg ${msg.id} in guild ${msg.guild.id}: ${ex}`,
                 );
                 if (html) {
-                    replace = `&lt;${animated ? "a" : ""}:${name}:${id}&gt;`;
+                    replace = `&lt;${animated ? "a" : ""}:${nameHtml}:${id}&gt;`;
                 } else {
                     replace = `<${animated ? "a" : ""}:${name}:${id}>`;
                 }
             }
-            content = content.replace(results[0],
-                replace);
+            content = content.replace(results[0], replace);
             results = MXC_INSERT_REGEX.exec(content);
         }
         return content;
     }
 
+    public async InsertChannelPills(content: string, msg: Discord.Message, html: boolean = false): Promise<string> {
+        let results = CHANNEL_INSERT_REGEX.exec(content);
+        while (results !== null) {
+            const id = results[ID_CHANNEL_INSERT_REGEX];
+            let replace = "";
+            const channel = msg.guild.channels.get(id);
+            if (channel) {
+                const alias = await this.opts.bot!.ChannelSyncroniser.GetAliasFromChannel(channel);
+                if (alias) {
+                    const name = "#" + channel.name;
+                    replace = html ? `<a href="${MATRIX_TO_LINK}${escapeHtml(alias)}">${escapeHtml(name)}</a>` : name;
+                }
+            }
+            if (!replace) {
+                replace = html ? `&lt;#${escapeHtml(id)}&gt;` : `<#${id}>`;
+            }
+            content = content.replace(results[0], replace);
+            results = CHANNEL_INSERT_REGEX.exec(content);
+        }
+        return content;
+    }
+
     private isEmbedInBody(msg: Discord.Message, embed: Discord.MessageEmbed): boolean {
         if (!embed.url) {
             return false;
@@ -304,8 +328,8 @@ export class DiscordMessageProcessor {
 
     private getDiscordParseCallbacks(msg: Discord.Message) {
         return {
-            channel: (node) => this.InsertChannel(node, msg),
-            emoji: (node) => this.InsertEmoji(node),
+            channel: (node) => this.InsertChannel(node), // are post-inserted
+            emoji: (node) => this.InsertEmoji(node), // are post-inserted
             everyone: (_) => this.InsertRoom(msg, "@everyone"),
             here: (_) => this.InsertRoom(msg, "@here"),
             role: (node) => this.InsertRole(node, msg),
@@ -315,7 +339,7 @@ export class DiscordMessageProcessor {
 
     private getDiscordParseCallbacksHTML(msg: Discord.Message) {
         return {
-            channel: (node) => this.InsertChannel(node, msg, true),
+            channel: (node) => this.InsertChannel(node), // are post-inserted
             emoji: (node) => this.InsertEmoji(node), // are post-inserted
             everyone: (_) => this.InsertRoom(msg, "@everyone"),
             here: (_) => this.InsertRoom(msg, "@here"),
diff --git a/test/test_channelsyncroniser.ts b/test/test_channelsyncroniser.ts
index 7d90f6e827c782c1ca6f4ce11d618af0dd6ed124..cbc338f4dd0a326d8f1d52e828fae54b59e33e48 100644
--- a/test/test_channelsyncroniser.ts
+++ b/test/test_channelsyncroniser.ts
@@ -1,5 +1,5 @@
 /*
-Copyright 2018 matrix-appservice-discord
+Copyright 2018, 2019 matrix-appservice-discord
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -83,6 +83,15 @@ function CreateChannelSync(remoteChannels: any[] = []): ChannelSyncroniser {
                             ALIAS_DELETED = true;
                         },
                         getStateEvent: async (mxid, event) => {
+                            if (event === "m.room.canonical_alias") {
+                                if (mxid === "!valid:localhost") {
+                                    return {
+                                        alias: "#alias:localhost",
+                                    };
+                                } else {
+                                    return null;
+                                }
+                            }
                             return event;
                         },
                         sendStateEvent: async (mxid, event, data) => {
@@ -278,6 +287,43 @@ describe("ChannelSyncroniser", () => {
             }
         });
     });
+    describe("GetAliasFromChannel", () => {
+        const getIds = async (chan) => {
+            if (chan.id === "678") {
+                return ["!valid:localhost"];
+            }
+            throw new Error("invalid");
+        };
+        it("Should get one canonical alias for a room", async () => {
+            const chan = new MockChannel();
+            chan.id = "678";
+            const channelSync = CreateChannelSync();
+            channelSync.GetRoomIdsFromChannel = getIds;
+            const alias = await channelSync.GetAliasFromChannel(chan as any);
+
+            expect(alias).to.equal("#alias:localhost");
+        });
+        it("Should return null if no alias found and no guild present", async () => {
+            const chan = new MockChannel();
+            chan.id = "123";
+            const channelSync = CreateChannelSync();
+            channelSync.GetRoomIdsFromChannel = getIds;
+            const alias = await channelSync.GetAliasFromChannel(chan as any);
+
+            expect(alias).to.equal(null);
+        });
+        it("Should return a #_discord_ alias if a guild is present", async () => {
+            const chan = new MockChannel();
+            const guild = new MockGuild("123");
+            chan.id = "123";
+            chan.guild = guild;
+            const channelSync = CreateChannelSync();
+            channelSync.GetRoomIdsFromChannel = getIds;
+            const alias = await channelSync.GetAliasFromChannel(chan as any);
+
+            expect(alias).to.equal("#_discord_123_123:localhost");
+        });
+    });
     describe("GetChannelUpdateState", () => {
         it("will do nothing on no rooms", async () => {
             const chan = new MockChannel();
diff --git a/test/test_discordmessageprocessor.ts b/test/test_discordmessageprocessor.ts
index 38649ddc80d55dcd301812710a477759d868e993..f78d66abded7dacaa4451b546796459cf7776e64 100644
--- a/test/test_discordmessageprocessor.ts
+++ b/test/test_discordmessageprocessor.ts
@@ -27,6 +27,14 @@ import { MockRole } from "./mocks/role";
 /* tslint:disable:no-unused-expression max-file-line-count no-any */
 
 const bot = {
+    ChannelSyncroniser: {
+        GetAliasFromChannel: async (chan) => {
+            if (chan.id === "456") {
+                return "#_discord_123_456:localhost";
+            }
+            return null;
+        },
+    },
     GetEmoji: async (name: string, animated: boolean, id: string): Promise<string> => {
         if (id === "3333333") {
             return "mxc://image";
@@ -348,37 +356,6 @@ describe("DiscordMessageProcessor", () => {
                 "<a href=\"https://matrix.to/#/@_discord_12345:localhost\">TestNickname</a>");
         });
     });
-    describe("InsertChannel / HTML", () => {
-        it("processes unknown channel correctly", () => {
-            const processor = new DiscordMessageProcessor(
-                new DiscordMessageProcessorOpts("localhost"), bot as DiscordBot);
-            const guild: any = new MockGuild("123", []);
-            const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"});
-            const msg = new MockMessage(channel) as any;
-            const content = { id: "123456789" };
-            let reply = processor.InsertChannel(content, msg);
-            Chai.assert.equal(reply, "<#123456789>");
-
-            reply = processor.InsertChannel(content, msg, true);
-            Chai.assert.equal(reply,
-                "&lt;#123456789&gt;");
-        });
-        it("processes channels correctly", () => {
-            const processor = new DiscordMessageProcessor(
-                new DiscordMessageProcessorOpts("localhost"), bot as DiscordBot);
-            const guild: any = new MockGuild("123", []);
-            const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"});
-            guild.channels.set("456", channel);
-            const msg = new MockMessage(channel) as any;
-            const content = { id: "456" };
-            let reply = processor.InsertChannel(content, msg);
-            Chai.assert.equal(reply, "#TestChannel");
-
-            reply = processor.InsertChannel(content, msg, true);
-            Chai.assert.equal(reply,
-                "<a href=\"https://matrix.to/#/#_discord_123_456:localhost\">#TestChannel</a>");
-        });
-    });
     describe("InsertRole / HTML", () => {
         it("ignores unknown roles", () => {
             const processor = new DiscordMessageProcessor(
@@ -424,7 +401,7 @@ describe("DiscordMessageProcessor", () => {
                 name: "blah",
             };
             const reply = processor.InsertEmoji(content);
-            Chai.assert.equal(reply, "\x01blah\x010\x011234\x01");
+            Chai.assert.equal(reply, "\x01emoji\x01blah\x010\x011234\x01");
         });
         it("inserts animated emojis to their post-parse flag", () => {
             const processor = new DiscordMessageProcessor(
@@ -435,7 +412,18 @@ describe("DiscordMessageProcessor", () => {
                 name: "blah",
             };
             const reply = processor.InsertEmoji(content);
-            Chai.assert.equal(reply, "\x01blah\x011\x011234\x01");
+            Chai.assert.equal(reply, "\x01emoji\x01blah\x011\x011234\x01");
+        });
+    });
+    describe("InsertChannel", () => {
+        it("inserts channels to their post-parse flag", () => {
+            const processor = new DiscordMessageProcessor(
+                new DiscordMessageProcessorOpts("localhost"), bot as DiscordBot);
+            const content = {
+                id: "1234",
+            };
+            const reply = processor.InsertChannel(content);
+            Chai.assert.equal(reply, "\x01chan\x011234\x01");
         });
     });
     describe("InsertMxcImages / HTML", () => {
@@ -445,7 +433,7 @@ describe("DiscordMessageProcessor", () => {
             const guild: any = new MockGuild("123", []);
             const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"});
             const msg = new MockMessage(channel) as any;
-            const content = "Hello \x01hello\x010\x01123456789\x01";
+            const content = "Hello \x01emoji\x01hello\x010\x01123456789\x01";
             let reply = await processor.InsertMxcImages(content, msg);
             Chai.assert.equal(reply, "Hello <:hello:123456789>");
 
@@ -459,13 +447,89 @@ describe("DiscordMessageProcessor", () => {
             const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"});
             guild.channels.set("456", channel);
             const msg = new MockMessage(channel) as any;
-            const content = "Hello \x01hello\x010\x013333333\x01";
+            const content = "Hello \x01emoji\x01hello\x010\x013333333\x01";
             let reply = await processor.InsertMxcImages(content, msg);
             Chai.assert.equal(reply, "Hello :hello:");
 
             reply = await processor.InsertMxcImages(content, msg, true);
             Chai.assert.equal(reply, "Hello <img alt=\"hello\" title=\"hello\" height=\"32\" src=\"mxc://image\" />");
         });
+        it("processes double-emoji correctly", async () => {
+            const processor = new DiscordMessageProcessor(
+                new DiscordMessageProcessorOpts("localhost"), bot as DiscordBot);
+            const guild: any = new MockGuild("123", []);
+            const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"});
+            guild.channels.set("456", channel);
+            const msg = new MockMessage(channel) as any;
+            const content = "Hello \x01emoji\x01hello\x010\x013333333\x01 \x01emoji\x01hello\x010\x013333333\x01";
+            let reply = await processor.InsertMxcImages(content, msg);
+            Chai.assert.equal(reply, "Hello :hello: :hello:");
+
+            reply = await processor.InsertMxcImages(content, msg, true);
+            Chai.assert.equal(reply, "Hello <img alt=\"hello\" title=\"hello\" height=\"32\" src=\"mxc://image\" /> " +
+                "<img alt=\"hello\" title=\"hello\" height=\"32\" src=\"mxc://image\" />");
+        });
+    });
+    describe("InsertChannelPills / HTML", () => {
+        it("processes unknown channel correctly", async () => {
+            const processor = new DiscordMessageProcessor(
+                new DiscordMessageProcessorOpts("localhost"), bot as DiscordBot);
+            const guild: any = new MockGuild("123", []);
+            const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"});
+            guild.channels.set("456", channel);
+            const msg = new MockMessage(channel) as any;
+            const content = "Hello \x01chan\x013333333\x01";
+            let reply = await processor.InsertChannelPills(content, msg);
+            Chai.assert.equal(reply, "Hello <#3333333>");
+
+            reply = await processor.InsertChannelPills(content, msg, true);
+            Chai.assert.equal(reply, "Hello &lt;#3333333&gt;");
+        });
+        it("processes channels correctly", async () => {
+            const processor = new DiscordMessageProcessor(
+                new DiscordMessageProcessorOpts("localhost"), bot as DiscordBot);
+            const guild: any = new MockGuild("123", []);
+            const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"});
+            guild.channels.set("456", channel);
+            const msg = new MockMessage(channel) as any;
+            const content = "Hello \x01chan\x01456\x01";
+            let reply = await processor.InsertChannelPills(content, msg);
+            Chai.assert.equal(reply, "Hello #TestChannel");
+
+            reply = await processor.InsertChannelPills(content, msg, true);
+            Chai.assert.equal(reply, "Hello <a href=\"https://matrix.to/#/#_discord_123" +
+                "_456:localhost\">#TestChannel</a>");
+        });
+        it("processes multiple channels correctly", async () => {
+            const processor = new DiscordMessageProcessor(
+                new DiscordMessageProcessorOpts("localhost"), bot as DiscordBot);
+            const guild: any = new MockGuild("123", []);
+            const channel = new Discord.TextChannel(guild, {id: "456", name: "TestChannel"});
+            guild.channels.set("456", channel);
+            const msg = new MockMessage(channel) as any;
+            const content = "Hello \x01chan\x01456\x01 \x01chan\x01456\x01";
+            let reply = await processor.InsertChannelPills(content, msg);
+            Chai.assert.equal(reply, "Hello #TestChannel #TestChannel");
+
+            reply = await processor.InsertChannelPills(content, msg, true);
+            Chai.assert.equal(reply, "Hello <a href=\"https://matrix.to/#/#_discord_123" +
+                "_456:localhost\">#TestChannel</a> <a href=\"https://matrix.to/#/#_discord_123" +
+                "_456:localhost\">#TestChannel</a>");
+        });
+        it("processes channels without alias correctly", async () => {
+            const processor = new DiscordMessageProcessor(
+                new DiscordMessageProcessorOpts("localhost"), bot as DiscordBot);
+            const guild: any = new MockGuild("123", []);
+            const channel = new Discord.TextChannel(guild, {id: "678", name: "TestChannel"});
+            guild.channels.set("678", channel);
+            const msg = new MockMessage(channel) as any;
+            const content = "Hello \x01chan\x01678\x01";
+            let reply = await processor.InsertChannelPills(content, msg);
+            Chai.assert.equal(reply, "Hello <#678>");
+
+            reply = await processor.InsertChannelPills(content, msg, true);
+            Chai.assert.equal(reply, "Hello &lt;#678&gt;");
+        });
     });
     describe("InsertEmbeds", () => {
         it("processes titleless embeds properly", () => {