Skip to content

Instantly share code, notes, and snippets.

@siveryt
Created February 9, 2025 21:19
Show Gist options
  • Save siveryt/f5d17cfc114a64679dc33f67beb9a472 to your computer and use it in GitHub Desktop.
Save siveryt/f5d17cfc114a64679dc33f67beb9a472 to your computer and use it in GitHub Desktop.
Buffl Exporter
const url = "https://api.buffl.co/api/cardsets/" + window.location.pathname.split("/")[3] + "/editable";
const headers = {
"Accept": "application/json, text/plain, */*",
"Authorization": localStorage.getItem("JWT_TOKEN")
};
// Fetch the data
fetch(url, { method: 'GET', headers: headers })
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json(); // Parse the JSON from the response
})
.then((data /** @type {BufflExport} */) => {
const choice = prompt("Do you want the full object, a simplified version, or a CSV table? (full/simplified/csv)");
let fileextension = '.json';
let filename = data.cardSet.title;
let blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
if (choice === 'simplified') {
data = {
title: data.cardSet.title,
cards: data.cardSet.cards.map(card => ({
question: card.front.content[0].content[0].text,
answer: card.back.content[0].content[0].text
}))
};
blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
} else if (choice === 'csv') {
const csvRows = [
['Question', 'Answer'],
...data.cardSet.cards.map(card => [card.front.content[0].content[0].text, card.back.content[0].content[0].text])
];
const csvContent = csvRows.map(e => e.join(";")).join("\n");
data = csvContent;
fileextension = '.csv';
blob = new Blob([data], { type: 'text/csv' });
}
// Create a blob from the JSON data
const url = window.URL.createObjectURL(blob);
// Create a link element
const a = document.createElement('a');
a.href = url;
a.download = filename + fileextension; // Set the file name
document.body.appendChild(a);
a.click(); // Programmatically click the link to trigger the download
a.remove(); // Remove the link from the document
window.URL.revokeObjectURL(url); // Clean up the URL object
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
});
// TYPEDEF
/**
* @typedef {Object} BufflExport
* @property {CardSet} cardSet
* @property {ACL} acl
*/
/**
* @typedef {Object} ACL
* @property {boolean} isStudent
* @property {boolean} isEditing
* @property {boolean} isOwner
*/
/**
* @typedef {Object} CardSet
* @property {string} title
* @property {string} owner
* @property {Course} course
* @property {string} type
* @property {boolean} isProgressing
* @property {number} card_inserts
* @property {number} card_updates
* @property {number} card_deletions
* @property {Card[]} cards
* @property {boolean} locked
* @property {boolean} preventDelete
* @property {boolean} publishedForSale
* @property {boolean} draft
* @property {boolean} deleted
* @property {Member[]} members
* @property {Date} updatedAt
* @property {Date} createdAt
* @property {ID} id
*/
/**
* @typedef {Object} Card
* @property {Config} hotspotConfig
* @property {Config} mcConfig
* @property {Config} sortConfig
* @property {GapConfig} gapConfig
* @property {CloudConfig} cloudConfig
* @property {Back} front
* @property {Back} back
* @property {string} answer_content
* @property {string} typeVersion
* @property {null} answer_image
* @property {null} answer_list
* @property {string} question_content
* @property {null} question_image
* @property {null} question_list
* @property {number} position
* @property {number} sec_position
* @property {number} answer_alignement
* @property {number} question_alignement
* @property {boolean} unActive
* @property {LegacyData} legacyData
* @property {ID} cardset
* @property {string} id
* @property {Date} [createdAt]
* @property {Date} [updatedAt]
*/
/**
* @typedef {Object} Back
* @property {BackType} type
* @property {Attrs} attrs
* @property {BackContent[]} content
*/
/**
* @typedef {Object} Attrs
* @property {number} version
*/
/**
* @typedef {Object} BackContent
* @property {FluffyType} type
* @property {ContentContent[]} content
*/
/**
* @typedef {Object} ContentContent
* @property {PurpleType} type
* @property {string} text
*/
/**
* @enum {string}
*/
const PurpleType = {
Text: "text",
};
/**
* @enum {string}
*/
const FluffyType = {
Paragraph: "paragraph",
};
/**
* @enum {string}
*/
const BackType = {
Doc: "doc",
};
/**
* @enum {string}
*/
const ID = {
The67A8C10Fd0D4D261258E3Ea0: "67a8c10fd0d4d261258e3ea0",
};
/**
* @typedef {Object} CloudConfig
* @property {any[]} answerFragments
* @property {any[]} falseOptions
*/
/**
* @typedef {Object} GapConfig
* @property {FillMethod} fillMethod
* @property {boolean} caseSensitive
* @property {any[]} answerFragments
* @property {any[]} falseOptions
*/
/**
* @enum {string}
*/
const FillMethod = {
FreeText: "FREE_TEXT",
};
/**
* @typedef {Object} Config
* @property {any[]} options
*/
/**
* @typedef {Object} LegacyData
* @property {string} question_content
* @property {string} answer_content
*/
/**
* @typedef {Object} Course
* @property {string} owner
* @property {any[]} editors
* @property {string} id
*/
/**
* @typedef {Object} Member
* @property {Preview} preview
* @property {string} role
* @property {string} member
* @property {string} type
* @property {string} id
*/
/**
* @typedef {Object} Preview
* @property {string} email
* @property {string} firstName
* @property {string} lastName
*/
@siveryt
Copy link
Author

siveryt commented Feb 9, 2025

You can add it to your Bookmark, when you copy the code over to my Bookmarklet Generator :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment