mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-27 03:57:21 +00:00
Introduce Twitter Archivist Plugin
This commit is contained in:
parent
344110e289
commit
fedc23d73c
@ -233,6 +233,15 @@ node $TW5_BUILD_TIDDLYWIKI \
|
|||||||
--build index \
|
--build index \
|
||||||
|| exit 1
|
|| exit 1
|
||||||
|
|
||||||
|
# /editions/twitter-archivist/index.html Twitter Archivist edition
|
||||||
|
node $TW5_BUILD_TIDDLYWIKI \
|
||||||
|
./editions/twitter-archivist \
|
||||||
|
--verbose \
|
||||||
|
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||||
|
--output $TW5_BUILD_OUTPUT/editions/twitter-archivist/ \
|
||||||
|
--build index \
|
||||||
|
|| exit 1
|
||||||
|
|
||||||
######################################################
|
######################################################
|
||||||
#
|
#
|
||||||
# Plugin demos
|
# Plugin demos
|
||||||
|
3
editions/twitter-archivist/tiddlers/DefaultTiddlers.tid
Normal file
3
editions/twitter-archivist/tiddlers/DefaultTiddlers.tid
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
title: $:/DefaultTiddlers
|
||||||
|
|
||||||
|
HelloThere
|
3
editions/twitter-archivist/tiddlers/HelloThere.tid
Normal file
3
editions/twitter-archivist/tiddlers/HelloThere.tid
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
title: HelloThere
|
||||||
|
|
||||||
|
{{$:/plugins/tiddlywiki/twitter-archivist/readme}}
|
3
editions/twitter-archivist/tiddlers/SiteSubtitle.tid
Normal file
3
editions/twitter-archivist/tiddlers/SiteSubtitle.tid
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
title: $:/SiteTitle
|
||||||
|
|
||||||
|
Get Your Tweets Into ~TiddlyWiki
|
3
editions/twitter-archivist/tiddlers/SiteTitle.tid
Normal file
3
editions/twitter-archivist/tiddlers/SiteTitle.tid
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
title: $:/SiteTitle
|
||||||
|
|
||||||
|
Twitter Archivist
|
16
editions/twitter-archivist/tiddlywiki.info
Normal file
16
editions/twitter-archivist/tiddlywiki.info
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"description": "Twitter Archivist Edition",
|
||||||
|
"plugins": [
|
||||||
|
"tiddlywiki/twitter-archivist"
|
||||||
|
],
|
||||||
|
"languages": [
|
||||||
|
],
|
||||||
|
"themes": [
|
||||||
|
"tiddlywiki/vanilla",
|
||||||
|
"tiddlywiki/snowwhite"
|
||||||
|
],
|
||||||
|
"build": {
|
||||||
|
"index": [
|
||||||
|
"--rendertiddler","$:/core/save/all","index.html","text/plain"]
|
||||||
|
}
|
||||||
|
}
|
264
plugins/tiddlywiki/twitter-archivist/archivist.js
Normal file
264
plugins/tiddlywiki/twitter-archivist/archivist.js
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
/*\
|
||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/archivist.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: utils
|
||||||
|
|
||||||
|
Utility class for manipulating Twitter archives
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global $tw: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function TwitterArchivist(options) {
|
||||||
|
options = options || {};
|
||||||
|
this.source = options.source;
|
||||||
|
}
|
||||||
|
|
||||||
|
TwitterArchivist.prototype.loadArchive = async function(options) {
|
||||||
|
options = options || {};
|
||||||
|
const wiki = options.wiki;
|
||||||
|
await this.source.init();
|
||||||
|
// Process the manifest and profile
|
||||||
|
const manifestData = await this.loadTwitterJsData("data/manifest.js","window.__THAR_CONFIG = ",""),
|
||||||
|
profileData = await this.loadTwitterJsData("data/profile.js","window.YTD.profile.part0 = ",""),
|
||||||
|
accountData = await this.loadTwitterJsData("data/account.js","window.YTD.account.part0 = ",""),
|
||||||
|
username = manifestData.userInfo.userName,
|
||||||
|
user_id = manifestData.userInfo.accountId;
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: "Twitter Archive for @" + username,
|
||||||
|
tags: "$:/tags/TwitterArchive",
|
||||||
|
user_id: user_id,
|
||||||
|
username: username,
|
||||||
|
displayname: manifestData.userInfo.displayName,
|
||||||
|
generation_date: $tw.utils.stringifyDate(new Date(manifestData.archiveInfo.generationDate)),
|
||||||
|
account_created_date: $tw.utils.stringifyDate(new Date(accountData[0].account.createdAt)),
|
||||||
|
bio: profileData[0].profile.description.bio,
|
||||||
|
website: profileData[0].profile.description.website,
|
||||||
|
location: profileData[0].profile.description.location
|
||||||
|
});
|
||||||
|
// Process the media
|
||||||
|
await this.source.processFiles("data/tweets_media","base64",function(mediaItem) {
|
||||||
|
var ext = mediaItem.filename.split(".").slice(-1)[0];
|
||||||
|
if("jpg png".split(" ").indexOf(ext) !== -1) {
|
||||||
|
var extensionInfo = $tw.utils.getFileExtensionInfo("." + ext),
|
||||||
|
type = extensionInfo ? extensionInfo.type : null;
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: "Tweet Media - " + mediaItem.filename,
|
||||||
|
tags: "$:/tags/TweetMedia",
|
||||||
|
status_id: mediaItem.filename.split("-")[0],
|
||||||
|
text: mediaItem.contents,
|
||||||
|
type: type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Process the favourites
|
||||||
|
const likeData = await this.loadTwitterJsData("data/like.js","window.YTD.like.part0 = ","");
|
||||||
|
$tw.utils.each(likeData,function(like) {
|
||||||
|
// Create the tweet tiddler
|
||||||
|
var tiddler = {
|
||||||
|
title: "Tweet - " + like.like.tweetId,
|
||||||
|
text: "\\rules only html entity extlink\n" + (like.like.fullText || "").replace("\n","<br>"),
|
||||||
|
status_id: like.like.tweetId,
|
||||||
|
liked_by: user_id,
|
||||||
|
tags: "$:/tags/Tweet"
|
||||||
|
};
|
||||||
|
wiki.addTiddler(tiddler);
|
||||||
|
});
|
||||||
|
// Process the tweets
|
||||||
|
const tweetData = await this.loadTwitterJsData("data/tweets.js","window.YTD.tweets.part0 = ","");
|
||||||
|
$tw.utils.each(tweetData,function(tweet) {
|
||||||
|
// Compile the tags for the tweet
|
||||||
|
var tags = ["$:/tags/Tweet"];
|
||||||
|
// Create tiddlers for each mentioned tweeter, and hyperlink the mention in the test
|
||||||
|
var rawText = tweet.tweet.full_text,
|
||||||
|
posText = 0,
|
||||||
|
text = "";
|
||||||
|
var mentions = [];
|
||||||
|
$tw.utils.each(tweet.tweet.entities.user_mentions,function(mention) {
|
||||||
|
var title = "Tweeter - " + mention.id_str;
|
||||||
|
tags.push(title);
|
||||||
|
wiki.addTiddler({
|
||||||
|
title: title,
|
||||||
|
screenname: "@" + mention.screen_name,
|
||||||
|
tags: "$:/tags/Tweeter",
|
||||||
|
user_id: mention.id_str,
|
||||||
|
name: mention.name
|
||||||
|
});
|
||||||
|
text = text +
|
||||||
|
$tw.utils.htmlEncode(rawText.substring(posText,mention.indices[0])) +
|
||||||
|
"<$link to=\"" + title + "\">" +
|
||||||
|
$tw.utils.htmlEncode(rawText.substring(mention.indices[0],mention.indices[1])) +
|
||||||
|
"</$link>";
|
||||||
|
posText = mention.indices[1];
|
||||||
|
mentions.push(mention.id_str);
|
||||||
|
});
|
||||||
|
if(posText < rawText.length) {
|
||||||
|
text = text + $tw.utils.htmlEncode(rawText.substring(posText));
|
||||||
|
}
|
||||||
|
text = text.replace("\n","<br>");
|
||||||
|
// Create the tweet tiddler
|
||||||
|
var tiddler = {
|
||||||
|
title: "Tweet - " + tweet.tweet.id_str,
|
||||||
|
text: "\\rules only html entity extlink\n" + text,
|
||||||
|
status_id: tweet.tweet.id_str,
|
||||||
|
user_id: user_id,
|
||||||
|
favorite_count: tweet.tweet.favorite_count,
|
||||||
|
retweet_count: tweet.tweet.retweet_count,
|
||||||
|
tags: tags,
|
||||||
|
created: $tw.utils.stringifyDate(new Date(tweet.tweet.created_at)),
|
||||||
|
modified: $tw.utils.stringifyDate(new Date(tweet.tweet.created_at))
|
||||||
|
};
|
||||||
|
if(tweet.tweet.in_reply_to_status_id_str) {
|
||||||
|
tiddler.in_reply_to_status_id = tweet.tweet.in_reply_to_status_id_str;
|
||||||
|
}
|
||||||
|
if(mentions.length > 0) {
|
||||||
|
tiddler.mention_user_ids = $tw.utils.stringifyList(mentions);
|
||||||
|
}
|
||||||
|
wiki.addTiddler(tiddler);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TwitterArchivist.prototype.loadTwitterJsData = async function(filePath,prefix,suffix) {
|
||||||
|
var tweetFileData = await this.source.loadTwitterJsData(filePath);
|
||||||
|
if(prefix) {
|
||||||
|
if(tweetFileData.slice(0,prefix.length) !== prefix) {
|
||||||
|
throw "Reading Twitter JS file " + filePath + " missing prefix '" + prefix + "'";
|
||||||
|
}
|
||||||
|
tweetFileData = tweetFileData.slice(prefix.length);
|
||||||
|
}
|
||||||
|
if(suffix) {
|
||||||
|
if(tweetFileData.slice(-suffix.length) !== suffix) {
|
||||||
|
throw "Reading Twitter JS file " + filePath + " missing suffix '" + suffix + "'";
|
||||||
|
}
|
||||||
|
tweetFileData = tweetFileData.slice(0,tweetFileData.length - suffix.length);
|
||||||
|
}
|
||||||
|
return JSON.parse(tweetFileData);
|
||||||
|
};
|
||||||
|
|
||||||
|
function TwitterArchivistSourceNodeJs(options) {
|
||||||
|
options = options || {};
|
||||||
|
this.archivePath = options.archivePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
TwitterArchivistSourceNodeJs.prototype.init = async function() {
|
||||||
|
};
|
||||||
|
|
||||||
|
TwitterArchivistSourceNodeJs.prototype.processFiles = async function(dirPath,encoding,callback) {
|
||||||
|
var fs = require("fs"),
|
||||||
|
path = require("path"),
|
||||||
|
dirPath = path.resolve(this.archivePath,dirPath),
|
||||||
|
filenames = fs.readdirSync(dirPath);
|
||||||
|
$tw.utils.each(filenames,function(filename) {
|
||||||
|
callback({
|
||||||
|
filename: filename,
|
||||||
|
contents: fs.readFileSync(path.resolve(dirPath,filename),encoding)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TwitterArchivistSourceNodeJs.prototype.loadTwitterJsData = async function(filePath) {
|
||||||
|
var fs = require("fs"),
|
||||||
|
path = require("path");
|
||||||
|
return fs.readFileSync(path.resolve(this.archivePath,filePath),"utf8");
|
||||||
|
};
|
||||||
|
|
||||||
|
function TwitterArchivistSourceBrowser(options) {
|
||||||
|
options = options || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
TwitterArchivistSourceBrowser.prototype.init = async function() {
|
||||||
|
// Open directory
|
||||||
|
this.rootDirHandle = await window.showDirectoryPicker();
|
||||||
|
};
|
||||||
|
|
||||||
|
TwitterArchivistSourceBrowser.prototype.processFiles = async function(dirPath,encoding,callback) {
|
||||||
|
const dirHandle = await this.walkDirectory(dirPath.split("/"));
|
||||||
|
for await (const [filename, fileHandle] of dirHandle.entries()) {
|
||||||
|
const contents = await fileHandle.getFile();
|
||||||
|
callback({
|
||||||
|
filename: filename,
|
||||||
|
contents: arrayBufferToBase64(await contents.arrayBuffer())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TwitterArchivistSourceBrowser.prototype.loadTwitterJsData = async function(filePath) {
|
||||||
|
const filePathParts = filePath.split("/");
|
||||||
|
const dirHandle = await this.walkDirectory(filePathParts.slice(0,-1));
|
||||||
|
const fileHandle = await dirHandle.getFileHandle(filePathParts.slice(-1)[0]);
|
||||||
|
const contents = await fileHandle.getFile();
|
||||||
|
return await contents.text();
|
||||||
|
};
|
||||||
|
|
||||||
|
TwitterArchivistSourceBrowser.prototype.walkDirectory = async function(arrayDirectoryEntries) {
|
||||||
|
var entries = arrayDirectoryEntries.slice(0),
|
||||||
|
dirHandle = this.rootDirHandle;
|
||||||
|
while(entries.length > 0) {
|
||||||
|
dirHandle = await dirHandle.getDirectoryHandle(entries[0]);
|
||||||
|
entries.shift();
|
||||||
|
}
|
||||||
|
return dirHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Thanks to MatheusFelipeMarinho
|
||||||
|
// https://github.com/MatheusFelipeMarinho/venom/blob/43ead0bfffa57a536a5cff67dd909e55da9f0915/src/lib/wapi/helper/array-buffer-to-base64.js#L55
|
||||||
|
function arrayBufferToBase64(arrayBuffer) {
|
||||||
|
var base64 = '';
|
||||||
|
var encodings =
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||||
|
|
||||||
|
var bytes = new Uint8Array(arrayBuffer);
|
||||||
|
var byteLength = bytes.byteLength;
|
||||||
|
var byteRemainder = byteLength % 3;
|
||||||
|
var mainLength = byteLength - byteRemainder;
|
||||||
|
|
||||||
|
var a, b, c, d;
|
||||||
|
var chunk;
|
||||||
|
|
||||||
|
// Main loop deals with bytes in chunks of 3
|
||||||
|
for (var i = 0; i < mainLength; i = i + 3) {
|
||||||
|
// Combine the three bytes into a single integer
|
||||||
|
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
||||||
|
|
||||||
|
// Use bitmasks to extract 6-bit segments from the triplet
|
||||||
|
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
|
||||||
|
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
|
||||||
|
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
|
||||||
|
d = chunk & 63; // 63 = 2^6 - 1
|
||||||
|
|
||||||
|
// Convert the raw binary segments to the appropriate ASCII encoding
|
||||||
|
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deal with the remaining bytes and padding
|
||||||
|
if (byteRemainder == 1) {
|
||||||
|
chunk = bytes[mainLength];
|
||||||
|
|
||||||
|
a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
|
||||||
|
|
||||||
|
// Set the 4 least significant bits to zero
|
||||||
|
b = (chunk & 3) << 4; // 3 = 2^2 - 1
|
||||||
|
|
||||||
|
base64 += encodings[a] + encodings[b] + '==';
|
||||||
|
} else if (byteRemainder == 2) {
|
||||||
|
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
|
||||||
|
|
||||||
|
a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
|
||||||
|
b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
|
||||||
|
|
||||||
|
// Set the 2 least significant bits to zero
|
||||||
|
c = (chunk & 15) << 2; // 15 = 2^4 - 1
|
||||||
|
|
||||||
|
base64 += encodings[a] + encodings[b] + encodings[c] + '=';
|
||||||
|
}
|
||||||
|
return base64;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.TwitterArchivist = TwitterArchivist;
|
||||||
|
exports.TwitterArchivistSourceNodeJs = TwitterArchivistSourceNodeJs;
|
||||||
|
exports.TwitterArchivistSourceBrowser = TwitterArchivistSourceBrowser;
|
||||||
|
|
||||||
|
})();
|
@ -0,0 +1,2 @@
|
|||||||
|
title: $:/config/TiddlerInfo/Mode
|
||||||
|
text: sticky
|
53
plugins/tiddlywiki/twitter-archivist/loadtwitterarchive.js
Normal file
53
plugins/tiddlywiki/twitter-archivist/loadtwitterarchive.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*\
|
||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/loadtwitterarchive.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: command
|
||||||
|
|
||||||
|
Read tiddlers from an unzipped Twitter archive
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global $tw: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var widget = require("$:/core/modules/widgets/widget.js");
|
||||||
|
|
||||||
|
exports.info = {
|
||||||
|
name: "loadtwitterarchive",
|
||||||
|
synchronous: false
|
||||||
|
};
|
||||||
|
|
||||||
|
var Command = function(params,commander,callback) {
|
||||||
|
this.params = params;
|
||||||
|
this.commander = commander;
|
||||||
|
this.callback = callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
Command.prototype.execute = function() {
|
||||||
|
var self = this;
|
||||||
|
if(this.params.length < 1) {
|
||||||
|
return "Missing path to Twitter archive";
|
||||||
|
}
|
||||||
|
var archivePath = this.params[0];
|
||||||
|
// Load tweets
|
||||||
|
var archiveSource = new $tw.utils.TwitterArchivistSourceNodeJs({
|
||||||
|
archivePath: archivePath
|
||||||
|
}),
|
||||||
|
archivist = new $tw.utils.TwitterArchivist({
|
||||||
|
source: archiveSource
|
||||||
|
});
|
||||||
|
archivist.loadArchive({
|
||||||
|
wiki: this.commander.wiki
|
||||||
|
}).then(function() {
|
||||||
|
self.callback(null);
|
||||||
|
}).catch(function(err) {
|
||||||
|
self.callback(err);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Command = Command;
|
||||||
|
|
||||||
|
})();
|
204
plugins/tiddlywiki/twitter-archivist/macros.tid
Normal file
204
plugins/tiddlywiki/twitter-archivist/macros.tid
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/macros
|
||||||
|
tags: $:/tags/Macro
|
||||||
|
|
||||||
|
\define skinny-tabs(tabNames,tabCaptions,defaultTab,state)
|
||||||
|
<$let
|
||||||
|
currTab={{{ [<__state__>get[text]else<__defaultTab__>] }}}
|
||||||
|
>
|
||||||
|
<div class="tc-tab-set">
|
||||||
|
<div class="tc-tab-buttons">
|
||||||
|
<$list filter="[enlist<__tabNames__>]" variable="tab" counter="tabCounter">
|
||||||
|
<$let
|
||||||
|
caption={{{ [enlist<__tabCaptions__>nth<tabCounter>] }}}
|
||||||
|
>
|
||||||
|
<$list filter="[<tab>match<currTab>]" variable="ignore">
|
||||||
|
<$button aria-checked="true" class="tc-tab-selected" role="switch">
|
||||||
|
<$action-setfield $tiddler=<<__state__>> $value=<<tab>>/>
|
||||||
|
<$text text=<<caption>>/>
|
||||||
|
</$button>
|
||||||
|
</$list>
|
||||||
|
<$list filter="[<tab>!match<currTab>]" variable="ignore">
|
||||||
|
<$button role="switch">
|
||||||
|
<$action-setfield $tiddler=<<__state__>> $value=<<tab>>/>
|
||||||
|
<$text text=<<caption>>/>
|
||||||
|
</$button>
|
||||||
|
</$list>
|
||||||
|
</$let>
|
||||||
|
</$list>
|
||||||
|
</div>
|
||||||
|
<div class="tc-tab-divider"></div>
|
||||||
|
<div class="tc-tab-content">
|
||||||
|
<$list filter="[enlist<__tabNames__>]" variable="tab" counter="tabCounter">
|
||||||
|
<$list filter="[<tab>match<currTab>]" variable="ignore">
|
||||||
|
<div class="tc-reveal">
|
||||||
|
<$macrocall $name=<<currTab>>/>
|
||||||
|
</div>
|
||||||
|
</$list>
|
||||||
|
<$list filter="[<tab>!match<currTab>]" variable="ignore">
|
||||||
|
<div class="tc-reveal" hidden="true"></div>
|
||||||
|
</$list>
|
||||||
|
</$list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</$let>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define list-archives()
|
||||||
|
\whitespace trim
|
||||||
|
<ul>
|
||||||
|
<$list filter="[tag[$:/tags/TwitterArchive]sort[displayname]]">
|
||||||
|
<li>
|
||||||
|
<$link><$text text=<<currentTiddler>>/></$link>
|
||||||
|
</li>
|
||||||
|
</$list>
|
||||||
|
</ul>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define show-archive()
|
||||||
|
<$let
|
||||||
|
user_id={{!!user_id}}
|
||||||
|
>
|
||||||
|
<div class="tc-twitter-archive">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<<show-archive-attribute "Username" "username" prefix:"@">>
|
||||||
|
<<show-archive-attribute "Display Name" "displayname">>
|
||||||
|
<<show-archive-attribute "Bio" "bio">>
|
||||||
|
<<show-archive-attribute "Location" "location">>
|
||||||
|
<<show-archive-attribute "Website" "website">>
|
||||||
|
<<show-archive-calculated-attribute "Number of Tweets" "[tag[$:/tags/Tweet]field:user_id<user_id>count[]]">>
|
||||||
|
<<show-archive-calculated-attribute "Number of Favorites Received" "[tag[$:/tags/Tweet]field:user_id<user_id>] :reduce[<currentTiddler>get[favorite_count]else[0]add<accumulator>]">>
|
||||||
|
<<show-archive-calculated-attribute "Number of Retweets Received" "[tag[$:/tags/Tweet]field:user_id<user_id>] :reduce[<currentTiddler>get[retweet_count]else[0]add<accumulator>]">>
|
||||||
|
<<show-archive-calculated-attribute "Number of Tweeters Mentioned" "[tag[$:/tags/Tweeter]count[]]">>
|
||||||
|
<<show-archive-attribute "User ID" "user_id">>
|
||||||
|
<<show-archive-attribute "Account Creation Date" "account_created_date" format:"date" template:"DDth mmm YYYY 0hh:0mm:0ss">>
|
||||||
|
<<show-archive-attribute "Archive Generation Date" "generation_date" format:"date" template:"DDth mmm YYYY 0hh:0mm:0ss">>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<$macrocall $name="skinny-tabs" tabNames="show-archive-tweets show-favorited-tweets" tabCaptions="Tweets Favourites" defaultTab="show-archive-tweets" state=<<qualify "$:/state/skinny-tabs/archive">>/>
|
||||||
|
</div>
|
||||||
|
</$let>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define show-archive-tweets()
|
||||||
|
<$let user_id={{!!user_id}}>
|
||||||
|
<$list filter="[tag[$:/tags/Tweet]field:user_id<user_id>!sort[created]limit[50]]">
|
||||||
|
<<show-tweet>>
|
||||||
|
</$list>
|
||||||
|
</$let>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define show-favorited-tweets()
|
||||||
|
<$let user_id={{!!user_id}}>
|
||||||
|
<$list filter="[tag[$:/tags/Tweet]field:liked_by<user_id>limit[50]]">
|
||||||
|
<<show-tweet>>
|
||||||
|
</$list>
|
||||||
|
</$let>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define show-archive-attribute(caption,field,prefix,format:"text",template)
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<$text text=<<__caption__>>/>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<$text text={{{ [<__prefix__>] }}}/>
|
||||||
|
<$view field=<<__field__>> format=<<__format__>> template=<<__template__>>/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define show-archive-calculated-attribute(caption,filter)
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<$text text=<<__caption__>>/>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<$text text={{{ [subfilter<__filter__>] }}}/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define show-tweet()
|
||||||
|
<div class="tc-twitter-tweet">
|
||||||
|
<div class="tc-twitter-tweet-header">
|
||||||
|
<$let user_id={{{ [<__archive__>get[user_id]] }}}>
|
||||||
|
<$list filter="[{!user_id}match<user_id>]" variable="ignore">
|
||||||
|
<span class="tc-twitter-tweet-header-displayname">
|
||||||
|
<$text text={{{ [<__archive__>get[displayname]] }}}/>
|
||||||
|
</span>
|
||||||
|
<span class="tc-twitter-tweet-header-username">
|
||||||
|
@<$text text={{{ [<__archive__>get[username]] }}}/>
|
||||||
|
</span>
|
||||||
|
•
|
||||||
|
</$list>
|
||||||
|
</$let>
|
||||||
|
<$link to=<<currentTiddler>>>
|
||||||
|
<span class="tc-twitter-tweet-header-date">
|
||||||
|
<$view field="created" format="date" template="DDth mmm YYYY 0hh:0mm:0ss"/>
|
||||||
|
</span>
|
||||||
|
</$link>
|
||||||
|
</div>
|
||||||
|
<$list filter="[<__title__>get[in_reply_to_status_id]addprefix[Tweet - ]is[tiddler]]" variable="replyTo">
|
||||||
|
<div class="tc-twitter-tweet-reply-to">
|
||||||
|
Reply to <$link to=<<replyTo>>><$text text=<<replyTo>>/></$link>
|
||||||
|
</div>
|
||||||
|
</$list>
|
||||||
|
<div class="tc-twitter-tweet-body">
|
||||||
|
<$transclude field="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="tc-twitter-tweet-media">
|
||||||
|
<$let status_id={{!!status_id}}>
|
||||||
|
<$list filter="[tag[$:/tags/TweetMedia]field:status_id<status_id>]" variable="mediaItem">
|
||||||
|
<$transclude tiddler=<<mediaItem>>/>
|
||||||
|
</$list>
|
||||||
|
</$let>
|
||||||
|
</div>
|
||||||
|
<div class="tc-twitter-tweet-footer">
|
||||||
|
<$list filter="[<currentTiddler>has[retweet_count]]" variable="ignore">
|
||||||
|
<span class="tc-twitter-tweet-footer-retweets">
|
||||||
|
Retweets: <$view field="retweet_count" format="text"/>
|
||||||
|
</span>
|
||||||
|
</$list>
|
||||||
|
<$list filter="[<currentTiddler>has[favorite_count]]" variable="ignore">
|
||||||
|
<span class="tc-twitter-tweet-footer-likes">
|
||||||
|
Likes: <$view field="favorite_count" format="text"/>
|
||||||
|
</span>
|
||||||
|
</$list>
|
||||||
|
<span class="tc-twitter-tweet-footer-twitter-link">
|
||||||
|
<a href={{{ [{!!status_id}addprefix[https://twitter.com/i/web/status/]] }}} rel="noopener noreferrer" target="_blank">View on Twitter</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define show-tweet-thread(archive)
|
||||||
|
<div class="tc-twitter-tweet-thread">
|
||||||
|
<$list filter="[<currentTiddler>has[in_reply_to_status_id]]" variable="ignore">
|
||||||
|
<div class="tc-twitter-tweet-reply">
|
||||||
|
<$tiddler tiddler={{{ [<currentTiddler>get[in_reply_to_status_id]addprefix[Tweet - ]] }}}>
|
||||||
|
<$macrocall $name="show-tweet"/>
|
||||||
|
</$tiddler>
|
||||||
|
</div>
|
||||||
|
</$list>
|
||||||
|
<$macrocall $name="show-tweet"/>
|
||||||
|
</div>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define show-tweeter()
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr><th>Username</th><td><$text text={{!!screenname}}/></td></tr>
|
||||||
|
<tr><th>Display Name</th><td><$text text={{!!name}}/></td></tr>
|
||||||
|
<tr><th>User ID</th><td><$text text={{!!user_id}}/></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<a href={{{ [{!!user_id}addprefix[https://twitter.com/intent/user?user_id=]] }}} rel="noopener noreferrer" target="_blank">View on Twitter</a>
|
||||||
|
<$macrocall $name="skinny-tabs" tabNames="show-tweeter-mentions" tabCaptions="Mentions" defaultTab="show-tweeter-mentions" state=<<qualify "$:/state/skinny-tabs/tweeter-mentions">>/>
|
||||||
|
\end
|
||||||
|
|
||||||
|
\define show-tweeter-mentions()
|
||||||
|
<$list filter="[tag[$:/tags/Tweet]tag<currentTiddler>]">
|
||||||
|
<$macrocall $name="show-tweet" archive=<<currentTiddler>> title=<<currentTiddler>>/>
|
||||||
|
</$list>
|
||||||
|
\end
|
6
plugins/tiddlywiki/twitter-archivist/plugin.info
Normal file
6
plugins/tiddlywiki/twitter-archivist/plugin.info
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"title": "$:/plugins/tiddlywiki/twitter-archivist",
|
||||||
|
"name": "Twitter Archivist",
|
||||||
|
"description": "Twitter archiving tools",
|
||||||
|
"list": "readme"
|
||||||
|
}
|
55
plugins/tiddlywiki/twitter-archivist/readme.tid
Normal file
55
plugins/tiddlywiki/twitter-archivist/readme.tid
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/readme
|
||||||
|
|
||||||
|
! Introduction
|
||||||
|
|
||||||
|
The Twitter Archivist imports the tweets and associated media from a [[Twitter Archive|https://help.twitter.com/en/managing-your-account/how-to-download-your-twitter-archive]] as individual tiddlers.
|
||||||
|
|
||||||
|
The Twitter Archivist plugin is available from the official plugin library for installation in your own wikis.
|
||||||
|
|
||||||
|
! Limitations
|
||||||
|
|
||||||
|
This initial version of the Twitter Archivist has several shortcomings that may be addressed in the future:
|
||||||
|
|
||||||
|
* Does not handle editable tweets
|
||||||
|
* Does not handle direct messages
|
||||||
|
|
||||||
|
! Limitations of Twitter Archives
|
||||||
|
|
||||||
|
The Twitter Archive format itself has many shortcomings which affect this tool:
|
||||||
|
|
||||||
|
* Retweets come through as old-school RTs, which means that they are often truncated
|
||||||
|
* Likes only have minimal information, lacking date, author and mentions
|
||||||
|
* External links go to the t.co shortener
|
||||||
|
* Twitter archives can be delivered in multiple parts, but this tool has only been tested with single archives. It is hoped that cumulatively importing each of the archives in turn should work
|
||||||
|
|
||||||
|
A future version of this tool may use the Twitter API to get around these restrictions.
|
||||||
|
|
||||||
|
! Getting Started
|
||||||
|
|
||||||
|
First, request your Tweet archive from Twitter. Once it is available, download and unzip it.
|
||||||
|
|
||||||
|
The Twitter Archivist can operate in the browser or under Node.js.
|
||||||
|
|
||||||
|
!! In the Browser
|
||||||
|
|
||||||
|
To import a Twitter archive in the browser, click the button below and navigate to the root of the archive:
|
||||||
|
|
||||||
|
<$button>
|
||||||
|
<$action-sendmessage $message="tm-load-twitter-archive"/>
|
||||||
|
Open Twitter archive
|
||||||
|
</$button>
|
||||||
|
|
||||||
|
!! Under Node.js
|
||||||
|
|
||||||
|
To import a Twitter archive under Node.js, use the `--loadtwitterarchive` command:
|
||||||
|
|
||||||
|
```
|
||||||
|
tiddlywiki editions/twitter-archivist/ --loadtwitterarchive '/path/to/archive' --build index
|
||||||
|
```
|
||||||
|
|
||||||
|
! Imported Archives
|
||||||
|
|
||||||
|
Any imported archives will show here:
|
||||||
|
|
||||||
|
<<list-archives>>
|
||||||
|
|
38
plugins/tiddlywiki/twitter-archivist/startup.js
Normal file
38
plugins/tiddlywiki/twitter-archivist/startup.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*\
|
||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/startup.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: startup
|
||||||
|
|
||||||
|
Twitter initialisation
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
/*jslint node: true, browser: true */
|
||||||
|
/*global $tw: false */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Export name and synchronous status
|
||||||
|
exports.name = "twitter-archivist";
|
||||||
|
exports.after = ["startup"];
|
||||||
|
exports.synchronous = true;
|
||||||
|
|
||||||
|
exports.startup = function() {
|
||||||
|
$tw.rootWidget.addEventListener("tm-load-twitter-archive",function(event) {
|
||||||
|
// Load tweets
|
||||||
|
var archiveSource = new $tw.utils.TwitterArchivistSourceBrowser({
|
||||||
|
}),
|
||||||
|
archivist = new $tw.utils.TwitterArchivist({
|
||||||
|
source: archiveSource
|
||||||
|
});
|
||||||
|
archivist.loadArchive({
|
||||||
|
wiki: $tw.wiki
|
||||||
|
}).then(function() {
|
||||||
|
alert("Archived tweets imported");
|
||||||
|
}).catch(function(err) {
|
||||||
|
alert("Error importing archived tweets: " + err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
47
plugins/tiddlywiki/twitter-archivist/styles.tid
Normal file
47
plugins/tiddlywiki/twitter-archivist/styles.tid
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/styles
|
||||||
|
tags: [[$:/tags/Stylesheet]]
|
||||||
|
code-body: yes
|
||||||
|
|
||||||
|
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock
|
||||||
|
|
||||||
|
.tc-twitter-tweet {
|
||||||
|
border: 1px solid <<colour muted-foreground>>;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tc-twitter-tweet-reply {
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tc-twitter-tweet-reply .tc-twitter-tweet {
|
||||||
|
margin: 0.5em 0 0.5em 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tc-twitter-tweet-header-displayname {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tc-twitter-tweet-header-username,
|
||||||
|
.tc-twitter-tweet-header-date {
|
||||||
|
color: #536471;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tc-twitter-tweet-reply-to {
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tc-twitter-tweet-body {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tc-twitter-tweet-reply .tc-twitter-tweet-body {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tc-twitter-tweet-footer {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/template/archive
|
||||||
|
|
||||||
|
<<show-archive>>
|
3
plugins/tiddlywiki/twitter-archivist/template-tweet.tid
Normal file
3
plugins/tiddlywiki/twitter-archivist/template-tweet.tid
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/template/tweet
|
||||||
|
|
||||||
|
<<show-tweet-thread>>
|
@ -0,0 +1,3 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/template/tweeter
|
||||||
|
|
||||||
|
<<show-tweeter>>
|
@ -0,0 +1,7 @@
|
|||||||
|
title: $:/plugins/tiddlywiki/twitter-archivist/view-template-body-cascade
|
||||||
|
tags: $:/tags/ViewTemplateBodyFilter
|
||||||
|
list-before:
|
||||||
|
|
||||||
|
[tag[$:/tags/Tweet]then[$:/plugins/tiddlywiki/twitter-archivist/template/tweet]]
|
||||||
|
[tag[$:/tags/TwitterArchive]then[$:/plugins/tiddlywiki/twitter-archivist/template/archive]]
|
||||||
|
[tag[$:/tags/Tweeter]then[$:/plugins/tiddlywiki/twitter-archivist/template/tweeter]]
|
Loading…
Reference in New Issue
Block a user