Little helper functions for E-mail notifications (RoomURLescape and GetReplayLink)



  • For those who want to build your own room links in email notifications here are two useful functions:

    function GetReplayLink(roomName, tick = Game.time, msg = "Replay"){
        return "<a href='https://screeps.com/a/#!/history/" +
               Game.shard.name + "/" + RoomURLescape(roomName) +
               "?t="+ tick +"'>" + msg +"</a>";
    }
    
    function RoomURLescape(roomName){
        let mapping = {N:"%4E", S:"%53", E:"%45",W:"%57"};
        let out = "";
        for (var i = 0; i < roomName.length; i++) {
            let c = roomName[i];
            out += mapping[c] || c;
        }
        return out;
    }
    

    If the room names aren't escaped the supposed server side "regex replace" kills your custom HTML links.
    However please keep in mind that this function only works for URLs and isn't suitable for anything else.

    hf 😉

    👍🖤💜


  • @mrfaul It's really helpful! Thank you sooo much



  • Ran into a wee issue when I tried to incorporate this into my rooms' general toString() which is then used in both logs & notifications - the game's notifier ended up malforming my links. Ended up just doing the same URL escape logic for HTML entities which prevents the game's notifier from automatically turning room names into links:

    // to-string.ts
    function roomToString(this: Room): string {
      return `<a href="${this.replayLink()}" style="color:#9c05a6ff">${this.htmlName}</a>`;
    }
    
    // replay-link.ts
    interface Ext {
      replayLink: typeof replayLink;
    
      /** Room name with HTML entities replacements to avoid it getting broken in notifications */
      get htmlName(): string;
    }
    
    declare global {
      interface Room extends Ext {
      }
    }
    
    Object.defineProperties(Room.prototype, {
      htmlName: lazyGetter('htmlName' satisfies keyof Room, function htmlName(this: Pick<Room, 'name' | 'htmlName'>): string {
        return escapeName(this.name, HTML_MAPPINGS);
      }),
      replayLink: confValue(replayLink),
    } satisfies TypeDescriptorMap<Ext>);
    
    function replayLink(this: Pick<Room, 'name'>, tick = Game.time): string {
      return `https://screeps.com/a/#!/history/${Game.shard.name}/${escapeName(this.name, URL_MAPPINGS)}?t=${tick}`;
    }
    
    const URL_MAPPINGS: Record<string, string> = {N: '%4E', S: '%53', E: '%45', W: '%57'};
    const HTML_MAPPINGS: Record<string, string> = {N: '&#78;', S: '&#83;', E: '&#69;', W: '&#87;'};
    
    function escapeName(roomName: string, mappings: Record<string, string>): string {
      let out = '';
    
      for (let i = 0, c: string; i < roomName.length; i++) {
        c = roomName[i]!;
        out += mappings[c] || c;
      }
    
      return out;
    }