이전에 배포했던 twitter형 채팅매크로가 커스텀시트 기능을 사용해야만 하는 것이 불편해서 양천일염님의 도움을 받아서(거의 다 작업해주셨습니다ㅠㅜ) api로 따로 제작했습니다. 여전히 PRO계정을 필요로 합니다만 API 스크립트에 추가만 해도 작동합니다.(게임 설정>API스크립트>New script에 복사 붙여넣기 하고 저장하시면 설치 완료입니다.)

 

양천일염님의 smallchat_split.js를 변경해서 만들었기 때문에 많은 변수이름을 공유하고 있습니다. 아마 따로 수정하지 않으면 같이 사용하시는 것은 힘들 것 같으므로 따로 사용해주세요. 물론 수정해서 같이 쓰셔도 됩니다.

 

양천일염님의 github 링크

 

저도 코딩을 전문적으로 공부하지 않았기 때문에 이 코드가 어떻게 돌아가는지는 잘 모릅니다.

☆★거의 다 양천일염님이 짜주셨어요!★☆ 감사합니다!

저는 간단한 아이디어를 내거나 트위터와 비슷하게 보이도록 출력 형태에만 손을 댔고 이것이 어떻게 구동되는지에 대해서는 잘 모릅니다. 그저 잘은 모르겠지만 이것이 돌아간다는 것에 기뻐하고 있는 수준의 이해도를 가지고 있음을 염두에 두어주세요. 즉, 질문하셔도 답변하지 못한다는 뜻입니다ㅠㅜ...

 

※실제 트위터와는 다르게 위에서 아래로 읽으며 내려가야하는 형태입니다.

 

사용방법

1. 이 api는 캐릭터의 이미지(정사각형이어야 동그랗게 나옵니다)를 트위터 프로필 이미지로 사용하며 캐릭터의 이름을 계정명으로 사용합니다. 계정 잠금을 표현하고 싶다면 🔒︎를 캐릭터 이름 뒤에 붙여넣어주세요.

2. 트위터의 계정 id는 플레이어의 이름을 가져옵니다. 다음과 같이 트위터 id를 입력하고 저장해주시면 됩니다. 

3. 멘션을 하고 싶은 경우 @(멘션을 하고 싶은 id) (하고 싶은 말)을 적어주시면 됩니다.

단, 이 명령어는 맨 앞이어야하고 영어가 아니면 파랗게 변하지 않음에 주의해주세요.

 

4. 이미지를 삽입하고 싶은 경우 [아무말](이미지주소)를 적어주시면 아래에 이미지가 출력됩니다. 이미지를 어느 위치에 적었든 아래쪽에 출력됩니다.

 

즐거운 세션 되시길 바라요!

 

2021.5.25 월 표시 수정.

2022.9.13 다크모드 활성화시에도 보이도록 닉네임 폰트색 설정 제거

출처표시 플러그인을 해제했기 때문에 이제는 아마 스크립트를 정상적으로 복사하실 수 있을 것입니다.

/* 원본 : 양천일염님의 smallchat_split.js https://github.com/kibkibe/roll20-api-scripts/tree/master/smallchat_split */
/* 이 코드는 양천일염님의 코드를 기반으로 만들어졌습니다. 대부분 양천일염님이 작업하셨고 저는 출력형식에만 조금 손을 댔습니다.*/
/* 배포를 허가해 주셔서 감사해요! */
/* (smallchat_twitter.js) 220913 코드 시작 */
on('ready', function() {
    if (!state.smallchatlog) state.smallchatlog = [];
    if (!state.smallchatonair) state.smallchatonair = [];
});
on("chat:message", function(msg)
{
if (msg.type == "api"){
    if (msg.content.indexOf("! ") === 0) {
        try {
        // option
        // 트윗 내역을 저장할 핸드아웃의 이름을 지정합니다.
        var logname = '(트위터)';
        // 실시간 트윗을 표시할 별도의 핸드아웃의 이름을 지정합니다.
        var onair_name = '(실시간 탐라)';
        // 실시간 트윗용 핸드아웃에서 최신순으로 몇개까지 채팅을 표시할지를 지정합니다.
        var onair_lines = 8;
        // 핸드아웃에 표시하는 채팅시각의 표준시간대를 지정합니다. 기본값은 KST(UTC+9)입니다.
        var timezone = 9;
  
        var ho = findObjs({ _type: 'handout', name:logname});
        var player = getObj('player',msg.playerid);
        
        var split;

        if (ho.length > 0) {
            ho = ho[0];
        } else {
            ho = createObj('handout', {
                notes: ' ',
                inplayerjournals: 'all',
                name: logname
            });
        }

       let content_str = msg.content.substring(2); 
       var res = content_str.match(/@\w+ /g);
       if (res) {
       for (var i=0;i<res.length;i++) {
        content_str = content_str.replace(res[i],"<span style='color:##58ACFA'>"+res[i]+"</span>");
       }
       }
var res_img = content_str.match(/\[.+\]\(http.+\)/g);
let img_str = "";
if (res_img) {
for (var i=0;i<res_img.length;i++) {
 content_str = content_str.replace(res_img[i],"");
 img_str += "<div style ='border-radius:15px;overflow:hidden;text-align:center;border:1px solid #C4CFD6;margin-top:10px;margin-left:60px;margin-right:10px;'><img src ='" 
 + res_img[i].replace(/\[.+\]\(/g,"").replace(")","") + "'></div>";
}
}
        var d = new Date();
        var tz = d.getTime() + (d.getTimezoneOffset() * 60000) + (timezone * 3600000);
        d.setTime(tz);
        var final_str = "<div width=70 style='border-radius:70%;overflow:hidden;valign:top;float:left;margin-right:10px'>"
                +"<img src='"+findCharacterWithName(msg.who).get('avatar')+"' width=50 height=50>" + "</div>"
                +"<div style='valign:top;'>"
                +"<span style='font-size:11pt'><b>"
                + msg.who + "</b>" + "</span>"
                + "<span style='color:#848484;font-size:10pt;'>" + " @"+player.get('_displayname') +" · "
                + d.getFullYear() + "년 " + (d.getMonth()+1) + "월 " + d.getDate() + "일 " + "</span>" 
                + "<div style='float:right;valign:top;'><img src='https://i.imgur.com/MONxPND.png'></div>" + "<br><div><span style='font-size:11pt;'>"
                + content_str
                +"</span><br><div style='clear:both;'>"
                + img_str
                +"<div style='margin-left:50px;float:left;width: 21%;'><img src='https://i.imgur.com/3n6ZKwZ.png'></div>"
                +"<div style='float:left; width: 21%;'><img src='https://i.imgur.com/Wt5bQ5S.png'></div>"
                +"<div style='float:left; width: 21%;'><img src='https://i.imgur.com/6mnK0lU.png'></div>"
                +"<div style='float:left;width: 21%;'><img src='https://i.imgur.com/WuZtI1V.png'></div>"
                +"</div>"+"<div style='border-bottom:1px solid #ebeef0;clear:both;'></div>";

            var oa = findObjs({ _type: 'handout', name:onair_name});
            if (oa.length > 0) {
                oa = oa[0];
            } else {
                oa = createObj('handout', {
                    notes: ' ',
                    inplayerjournals: 'all',
                    name: onair_name
                });
            }
            oa.get('notes',function(text) {
                var final_split = (text.length > 0 && text != 'null' ? text.split("<br><i></i>") : []);
                final_split.push(final_str);
                if (final_split.length > onair_lines) {
                    final_split.splice(0,1);
                }
                state.smallchatonair.push(final_split.join("<br><i></i>"));
                if (state.smallchatonair.length > 0) {
                    oa.set({notes: state.smallchatonair[0]});
                    state.smallchatonair.splice(0,1);
                }
            });
        
        ho.get('notes',function(text) {
            state.smallchatlog.push((text.length > 0 && text != 'null' ? text : "") + "<br><i></i>" + final_str);
            if (state.smallchatlog.length > 0) {
                ho.set({notes: state.smallchatlog[0]});
                state.smallchatlog.splice(0,1);
            }
        });
	} catch (error) {
        sendChat('error','/w GM '+error,null,{noarchive:true});
	}
    }
}
});
const findCharacterWithName = function(who) {
    let chat_cha = findObjs({ _type: 'character', name: who});
    return (chat_cha.length > 0) ? chat_cha[0] : null;
};
/* (smallchat_twitter.js) 220913 코드 종료 */

 

 

2022.9.13

채팅창에도 함께 출력되도록 수정한 버전을 올려둡니다. 

배경을 흰색으로 만들지 못해서 그것이 조금 아쉽네요.

채팅창의 사이즈를 적절히 늘려주셔야 트위터처럼 보입니다.

/* 원본 : 양천일염님의 smallchat_split.js https://github.com/kibkibe/roll20-api-scripts/tree/master/smallchat_split */
/* 이 코드는 양천일염님의 코드를 기반으로 만들어졌습니다. 대부분 양천일염님이 작업하셨고 저는 출력형식에만 조금 손을 댔습니다.*/
/* 배포를 허가해 주셔서 감사해요! */
/* (smallchat_twitter_both.js) 220913 코드 시작 */
on('ready', function() {
    if (!state.smallchatlog) state.smallchatlog = [];
    if (!state.smallchatonair) state.smallchatonair = [];
});
on("chat:message", function(msg)
{
if (msg.type == "api"){
    if (msg.content.indexOf("! ") === 0) {
        try {
        // option
        // 트윗 내역을 저장할 핸드아웃의 이름을 지정합니다.
        var logname = '(트위터)';
        // 실시간 트윗을 표시할 별도의 핸드아웃의 이름을 지정합니다.
        var onair_name = '(실시간 탐라)';
        // 실시간 트윗용 핸드아웃에서 최신순으로 몇개까지 채팅을 표시할지를 지정합니다.
        var onair_lines = 8;
        // 핸드아웃에 표시하는 채팅시각의 표준시간대를 지정합니다. 기본값은 KST(UTC+9)입니다.
        var timezone = 9;
  
        var ho = findObjs({ _type: 'handout', name:logname});
        var player = getObj('player',msg.playerid);
        
        var split;

        if (ho.length > 0) {
            ho = ho[0];
        } else {
            ho = createObj('handout', {
                notes: ' ',
                inplayerjournals: 'all',
                name: logname
            });
        }

       let content_str = msg.content.substring(2); 
       var res = content_str.match(/@\w+ /g);
       if (res) {
       for (var i=0;i<res.length;i++) {
        content_str = content_str.replace(res[i],"<span style='color:##58ACFA'>"+res[i]+"</span>");
       }
       }
var res_img = content_str.match(/\[.+\]\(http.+\)/g);
let img_str = "";
if (res_img) {
for (var i=0;i<res_img.length;i++) {
 content_str = content_str.replace(res_img[i],"");
 img_str += "<div style ='border-radius:15px;overflow:hidden;text-align:center;border:1px solid #C4CFD6;margin-top:10px;margin-left:60px;margin-right:10px;'><img src ='" 
 + res_img[i].replace(/\[.+\]\(/g,"").replace(")","") + "'></div>";
}
}
        var d = new Date();
        var tz = d.getTime() + (d.getTimezoneOffset() * 60000) + (timezone * 3600000);
        d.setTime(tz);
        var final_str = "<div width=70 style='border-radius:70%;overflow:hidden;valign:top;float:left;margin-right:10px'>"
                +"<img src='"+findCharacterWithName(msg.who).get('avatar')+"' width=50 height=50>" + "</div>"
                +"<div style='valign:top;'>"
                +"<span style='font-size:11pt'><b>"
                + msg.who + "</b>" + "</span>"
                + "<span style='color:#848484;font-size:10pt;'>" + " @"+player.get('_displayname') +" · "
                + d.getFullYear() + "년 " + (d.getMonth()+1) + "월 " + d.getDate() + "일 " + "</span>" 
                + "<div style='float:right;valign:top;'><img src='https://i.imgur.com/MONxPND.png'></div>" + "<br><div><span style='font-size:11pt;'>"
                + content_str
                +"</span><br><div style='clear:both;'>"
                + img_str
                +"<div style='margin-left:50px;float:left;width: 21%;'><img src='https://i.imgur.com/3n6ZKwZ.png'></div>"
                +"<div style='float:left; width: 21%;'><img src='https://i.imgur.com/Wt5bQ5S.png'></div>"
                +"<div style='float:left; width: 21%;'><img src='https://i.imgur.com/6mnK0lU.png'></div>"
                +"<div style='float:left;width: 21%;'><img src='https://i.imgur.com/WuZtI1V.png'></div>"
                +"</div>"+"<div style='border-bottom:1px solid #ebeef0;clear:both;'></div>";

            var oa = findObjs({ _type: 'handout', name:onair_name});
            if (oa.length > 0) {
                oa = oa[0];
            } else {
                oa = createObj('handout', {
                    notes: ' ',
                    inplayerjournals: 'all',
                    name: onair_name
                });
            }
            oa.get('notes',function(text) {
                var final_split = (text.length > 0 && text != 'null' ? text.split("<br><i></i>") : []);
                final_split.push(final_str);
                if (final_split.length > onair_lines) {
                    final_split.splice(0,1);
                }
                state.smallchatonair.push(final_split.join("<br><i></i>"));
                if (state.smallchatonair.length > 0) {
                    oa.set({notes: state.smallchatonair[0]});
                    state.smallchatonair.splice(0,1);
                }
            });
        
        ho.get('notes',function(text) {
            state.smallchatlog.push((text.length > 0 && text != 'null' ? text : "") + "<br><i></i>" + final_str);
            if (state.smallchatlog.length > 0) {
                ho.set({notes: state.smallchatlog[0]});
                state.smallchatlog.splice(0,1);
            }
        });
sendChat('',"%NEWLINE%"+final_str,null,{noarchive:false});
	} catch (error) {
        sendChat('error','/w GM '+error,null,{noarchive:true});
	}
    }
}
});
const findCharacterWithName = function(who) {
    let chat_cha = findObjs({ _type: 'character', name: who});
    return (chat_cha.length > 0) ? chat_cha[0] : null;
};
/* (smallchat_twitter_both.js) 220913 코드 종료 */

 

2022.9.13

채팅창에만 출력되도록 수정한 버전도 올려둡니다.

배경을 흰색으로 만들지 못해서 그것이 조금 아쉽네요.

채팅창의 사이즈를 적절히 늘려주셔야 트위터처럼 보입니다.

/* 원본 : 양천일염님의 smallchat_split.js https://github.com/kibkibe/roll20-api-scripts/tree/master/smallchat_split */
/* 이 코드는 양천일염님의 코드를 기반으로 만들어졌습니다. 대부분 양천일염님이 작업하셨고 저는 출력형식에만 조금 손을 댔습니다.*/
/* 배포를 허가해 주셔서 감사해요! */
/* (smallchat_twitter_onlychat.js) 220913 코드 시작 */

on("chat:message", function(msg)
{
if (msg.type == "api"){
    if (msg.content.indexOf("! ") === 0) {
        try {
        // option      
        // 핸드아웃에 표시하는 채팅시각의 표준시간대를 지정합니다. 기본값은 KST(UTC+9)입니다.
        var timezone = 9;
        var player = getObj('player',msg.playerid);
         

       let content_str = msg.content.substring(2); 
       var res = content_str.match(/@\w+ /g);
       if (res) {
       for (var i=0;i<res.length;i++) {
        content_str = content_str.replace(res[i],"<span style='color:##58ACFA'>"+res[i]+"</span>");
       }
       }
var res_img = content_str.match(/\[.+\]\(http.+\)/g);
let img_str = "";
if (res_img) {
for (var i=0;i<res_img.length;i++) {
 content_str = content_str.replace(res_img[i],"");
 img_str += "<div style ='border-radius:15px;overflow:hidden;text-align:center;border:1px solid #C4CFD6;margin-top:10px;margin-left:60px;margin-right:10px;'><img src ='" 
 + res_img[i].replace(/\[.+\]\(/g,"").replace(")","") + "'></div>";
}
}
        var d = new Date();
        var tz = d.getTime() + (d.getTimezoneOffset() * 60000) + (timezone * 3600000);
        d.setTime(tz);
        var final_str = "<div width=70 style='border-radius:70%;overflow:hidden;valign:top;float:left;margin-right:10px'>"
                +"<img src='"+findCharacterWithName(msg.who).get('avatar')+"' width=50 height=50>" + "</div>"
                +"<div style='valign:top;'>"
                +"<span style='font-size:11pt'><b>"
                + msg.who + "</b>" + "</span>"
                + "<span style='color:#848484;font-size:10pt;'>" + " @"+player.get('_displayname') +" · "
                + d.getFullYear() + "년 " + (d.getMonth()+1) + "월 " + d.getDate() + "일 " + "</span>" 
                + "<div style='float:right;valign:top;'><img src='https://i.imgur.com/MONxPND.png'></div>" + "<br><div><span style='font-size:11pt;'>"
                + content_str
                +"</span><br><div style='clear:both;'>"
                + img_str
                +"<div style='margin-left:50px;float:left;width: 21%;'><img src='https://i.imgur.com/3n6ZKwZ.png'></div>"
                +"<div style='float:left; width: 21%;'><img src='https://i.imgur.com/Wt5bQ5S.png'></div>"
                +"<div style='float:left; width: 21%;'><img src='https://i.imgur.com/6mnK0lU.png'></div>"
                +"<div style='float:left;width: 21%;'><img src='https://i.imgur.com/WuZtI1V.png'></div>"
                +"</div>"+"<div style='border-bottom:1px solid #ebeef0;clear:both;'></div>";

sendChat('',"%NEWLINE%"+final_str,null,{noarchive:false});
	} catch (error) {
        sendChat('error','/w GM '+error,null,{noarchive:true});
	}
    }
}
});
const findCharacterWithName = function(who) {
    let chat_cha = findObjs({ _type: 'character', name: who});
    return (chat_cha.length > 0) ? chat_cha[0] : null;
};
/* (smallchat_twitter_onlychat.js) 220913 코드 종료 */

 

 

+ Recent posts