Wait, don't look down on me I'ma put you in your place
I'ma knife you in your spleen, I'ma punch you in the face
Bitch don't go slow I'ma go my own pace
I'ma do my own shit, I'ma tie my own lace
When I'm telling you my day look me in my fucking face
I just wanna be the best, all these bitches fucking fake
Don't waste my time, I'ma do my own thing

I told a bitch pipe down, I got bitch on my line
She wanna fuck with me all night like it's Saturday night
I'm tryna party every day, cause I'm tired of life
I hope this shit is just a phase, if it's not I'll just die

Hold on let me tell you a little story about motherfucking pack
I'ma shot you from the back, mutherfucker talking trash
If you wanna talk shit, talk to my mag
I ain't tryna be like that, I just wanna be the best
I'm just tryna get a bag, like bitch like that

Driving round in the city, looking at your face
With that motherfucking pity
I got a bitch so bad I be sucking on her titty
Got a silencer when I shoot at your kidney like damn

Wait hold up, goth bitch she suck my soda
She like fire, she wanna roll up, she wanna beat you up with a totem
Make you cry when she call you brother
Pick me bitch, but that grip gone hold up like bitch like dam
If you wanna fuck then that's a problem
B.D.E tell a bitch they motherfucking bottom

Fell down so hard I'm smoking T.H.C, I got up now I play you like a ps3
I got no time, for these bitch
If you wanna talk to me, talk business (yuh yuh)
I got put in a bad light
Started from the fucking bottom, now I'm in sight
Now I'm talking these bitches that I don't like
If you tryna be with me that's a big sike
Like bitch like damn

I told a bitch pipe down, I got bitch on my line
She wanna fuck with me all night like it's Saturday night
I'm tryna party every day, cause I'm tired of life
I hope this shit is just a phase, if it's not I'll just die

I told a bitch pipe down, I got bitch on my line
She wanna fuck with me all night like it's Saturday night
I'm tryna party every day, cause I'm tired of life
I hope this shit is just a phase, if it's not I'll just die

const express = require('express')
const sqlite3 = require('sqlite3')
const fs = require('fs')
const path = require('path')

const themify = require('./lib/themify')
const pushDB = require('./lib/push')

const config = require('../config.json')
console.log('Your config.json file:')
console.log(config)
const root = config.root
const port = config.port
const title = config.title
const db_file = config.db

const app = express()
app.set('view engine', 'ejs')
app.set('views', path.join(__dirname, 'views'))
app.set('trust proxy', true)
app.use(root, express.static(path.join(__dirname, 'static')))

const db = new sqlite3.Database(db_file)
const create_db_query = `CREATE TABLE flagcounter (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    ip TEXT,
    country TEXT CHECK(length(country) <= 2)
);`

if (!fs.existsSync(db_file)) {
    db.run(create_db_query, (err) => {
        if (!err) {
            console.log(
                `Database \"${db_file}\" not found. Database and table created successfully.`
            )
        } else {
            console.error(err)
        }
    })
} else {
    console.log(`The database \"${db_file}\" already exists!`)
    db.get('SELECT COUNT(*) AS count FROM flagcounter', (err, count) => {
        if (!err) {
            console.log('Number of entries found in the database:', count.count)
        } else {
            console.error(err)
        }
    })
}

app.get(root, async (req, res) => {
    res.render("index", {
        title,
        root
    })
})

app.get(`${root}counter`, async (req, res) => {
    let { theme = 'moebooru' } = req.query
    let demo = req.query.demo
    let digits = req.query.digits

    db.all('SELECT * FROM flagcounter', (err, rows) => {
        if (!err) {
            let count = demo ? 123456780 : Number(new Set(rows.map(entry => entry.ip)).size)
            let length = digits ? digits : String(count).length
            let renderSvg = themify.getCountImage({ count, theme, length })
            
            res
                .set({
                    'content-type': 'image/svg+xml',
                    'cache-control': 'max-age=0, no-cache, no-store, must-revalidate'
                })
                .send(renderSvg)
        } else {
            console.error(err)
            res.status(500).render("error", {
                title: "HTTP Error 500",
                desc: "Internal Server Error",
                root
            })
        }
    })

    pushDB(req)
})

app.get(`${root}flags`, async (req, res) => {
    db.all('SELECT * FROM flagcounter', (err, rows) => {
        if (!err) {
            res.render("flags", {
                title,
                root,
                rows
            })
        } else {
            console.error(err)
            res.status(500).render("reply", {
                title: "HTTP Error 500",
                desc: "Internal Server Error",
                root
            })
        }
    })
})

app.use((req, res, next) => {
    res.status(404).render('error', {
        title: 'HTTP Error 404',
        desc: 'You have probably lost yourself... This page does not exist.',
        root
    })
})

app.listen(port, () => {
    console.log(`Web Server is available at http://localhost:${port}${root}.`)
})

import qbittorrentapi as qb
import time
import math


qbt_client = qb.Client(
    host="",
    port=,
    username="",
    password="")


try:
    qbt_client.auth_log_in()
except qb.LoginFailed as e:
    print(e)
    

# https://stackoverflow.com/a/14822210
def convert_size(size_bytes):
   if size_bytes == 0:
       return "0B"
   size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
   i = int(math.floor(math.log(size_bytes, 1024)))
   p = math.pow(1024, i)
   s = round(size_bytes / p, 2)
   return "%s %s" % (s, size_name[i])
        


# Console starts here
print("\nqBittorrent Stats\n")

print(f"Connection Status: {qbt_client.transfer_info()['connection_status'].capitalize()}")
print(f"Session Upload: {convert_size(size_bytes=qbt_client.transfer_info()['up_info_data'])}")
print(f"Session Download: {convert_size(size_bytes=qbt_client.transfer_info()['dl_info_data'])}")


if qbt_client.transfer_info()['up_rate_limit'] == 0:
    print("UP Rate limit: None")
else:
    print(f"UP Rate limit: {convert_size(size_bytes=qbt_client.transfer_info()['up_rate_limit'])}/s")


if qbt_client.transfer_info()['dl_rate_limit'] == 0:
    print("DL Rate limit: None\n")
else:
    print(f"DL Rate limit: {convert_size(size_bytes=qbt_client.transfer_info()['dl_rate_limit'])}/s\n")
    

while True:
    if qbt_client.transfer_info()['connection_status'] == "disconnected":
        print("qBittorrent is disconnected.")
        break
    else:
        up = qbt_client.transfer_info()["up_info_speed"]
        dl = qbt_client.transfer_info()["dl_info_speed"]
        
        up = convert_size(size_bytes=up)
        dl = convert_size(size_bytes=dl)
        
        upstr = f"Upload: {up}/s"
        dlstr = f"Download: {dl}/s"
        
        print(upstr.ljust(20, " ") + " |  " + dlstr)
        
        time.sleep(1.5)

const router = require('express').Router()
const fs = require('fs')
const yaml = require('js-yaml')

const version = JSON.parse(fs.readFileSync('package.json')).version
const config = yaml.load(fs.readFileSync('config.yml', 'utf8'))
const title = config.title
const websites = config.websites
const desc = config.desc
const permalink = config.permalink
const copyright = config.copyright
const track = config.track

const findAvailableIndexAdjacent = require('../lib/findAvailableIndexAdjacent')
const findAvailableIndexExtreme = require('../lib/findAvailableIndexExtreme')
const findAllAvailable = require('../lib/findAllAvailable')
const getPrettyDate = require('../lib/getPrettyDate')

const startTime = Date.now()

router.get('/redirect', async (req, res) => {
    let website = req.query.website
    let action = req.query.action
    let index = websites.findIndex(site => site.slug === website)

    // if the action is random and there is no website param, it will just select a random working site
    // good for putting this on the index site or another site that isn't part of the ring itself
    if (action === 'random' && !website) {
        let availableWebsites = await findAllAvailable(websites)

        if (availableWebsites.length > 0) {
            let random = websites[availableWebsites[Math.floor(Math.random() * availableWebsites.length)]].url
            console.log(`${getPrettyDate(new Date())}: [${req.header('CF-Connecting-IP') ? req.header('CF-Connecting-IP') : req.ip}]: randomNoCheck --> ${random}`)
            res.redirect(random)
        } else {
            console.error(`${getPrettyDate(new Date())}: [${req.header('CF-Connecting-IP') ? req.header('CF-Connecting-IP') : req.ip}]: randomNoCheck --> sendBrickedWebring`)
            sendBrickedWebring(res)
        }
    
    // because website is set, we will remove its index to prevent random from redirecting it to itself
    } else if (action === 'random' && websites.some(site  => site.slug === website)) {

        let availableWebsites = await findAllAvailable(websites)

        let indexToRemove = availableWebsites.indexOf(index)
        if (indexToRemove !== -1) {
            availableWebsites.splice(indexToRemove, 1)
        }

        if (availableWebsites.length > 0) {
            let random = websites[availableWebsites[Math.floor(Math.random() * availableWebsites.length)]].url
            console.log(`${getPrettyDate(new Date())}: [${req.header('CF-Connecting-IP') ? req.header('CF-Connecting-IP') : req.ip}]: randomWithCheck --> ${random}`)
            res.redirect(random)
        } else {
            console.error(`${getPrettyDate(new Date())}: [${req.header('CF-Connecting-IP') ? req.header('CF-Connecting-IP') : req.ip}]: randomWithCheck --> sendBrickedWebring`)
            sendBrickedWebring(res)
        }

    // if the action is prev or next then it must have the website included and it must be part of the webring!
    } else if ((action === 'prev' || action === 'next') && websites.some(site  => site.slug === website)) {
        let adjIndex

        if (action === 'prev') {
            adjIndex = await findAvailableIndexAdjacent(index, websites, 'prev')
        } else if (action === 'next') {
            adjIndex = await findAvailableIndexAdjacent(index, websites, 'next')
        }

        // check if next/prev isn't the same as the current site, if it is send bricked webring
        if (adjIndex !== index) {
            let url = websites[adjIndex].url
            console.log(`${getPrettyDate(new Date())}: [${req.header('CF-Connecting-IP') ? req.header('CF-Connecting-IP') : req.ip}]: prev/next --> ${url}`)
            res.redirect(url)
        } else {
            console.error(`${getPrettyDate(new Date())}: [${req.header('CF-Connecting-IP') ? req.header('CF-Connecting-IP') : req.ip}]: prev/next --> sendBrickedWebring`)
            sendBrickedWebring(res)
        }

    // here we don't care if website is set or not, just ignore it, take the user to the last or first available
    // website, good to put on index like the random w/o checking if its the same website
    } else if (action === 'first' || action === 'last') {
        let xtrIndex

        if (action === 'first') {
            xtrIndex = await findAvailableIndexExtreme(websites, 'first')
        } else if (action === 'last') { 
            xtrIndex = await findAvailableIndexExtreme(websites, 'last')
        }

        // but we have to change this, ignoring if xtrIndex === index and only sending bricked webring if there's no xtrIndex at all
        if (xtrIndex !== -1) {
            let url = websites[xtrIndex].url
            console.log(`${getPrettyDate(new Date())}: [${req.header('CF-Connecting-IP') ? req.header('CF-Connecting-IP') : req.ip}]: first/last --> ${url}`)
            res.redirect(url)
        } else {
            console.error(`${getPrettyDate(new Date())}: [${req.header('CF-Connecting-IP') ? req.header('CF-Connecting-IP') : req.ip}]: first/last --> sendBrickedWebring`)
            sendBrickedWebring(res)
        }

    } else {
        console.error(`${getPrettyDate(new Date())}: [${req.header('CF-Connecting-IP') ? req.header('CF-Connecting-IP') : req.ip}]: Redirect Param Error`)
        res.status(400).render('error', {
            title,
            errorTitle: 'Redirect Param Error',
            startTime,
            version,
            permalink,
            copyright,
            track,
            error: `
                <h3>Possible reasons for this error:</h3>
                <ul class="error">
                    <li>The action param was "random" but the website param's value isn't in the Webring.</li>
                    <li>The action param was "prev" or "next" but the website param's value isn't in the Webring.</li>
                    <li&gt>The action param wasn't "first", "last" or "random".</li>
                    <li&gt>You're severely retarded and discovered a new way to fuck up.</li>
                    </ul>
            `,
            desc
        })
    }
})

async function sendBrickedWebring(res) {
    res.status(500).render('error', {
        title,
        errorTitle: 'Bricked Webring Error',
        version,
        permalink,
        startTime,
        copyright,
        track,
        error: `
            <h3>Possible reasons for this error:</h3>
            <p>Every website checked had some sort of error...</p>
            <ul class="error">
                <li>Either they are all dead.</li>
                <li>Either they are all misconfigured.</li>
                <li>Or both!</li>
                <li>There is also the possibility that the last website you visited was the only one alive at the time.</li>
            </ul>
            <p>The webring is possibly completely bricked, sorry about that.</p>
        `,
        desc
    })
}

module.exports = router

const axios = require('axios')
const cheerio = require('cheerio')

/**
    * Represents a scrapper for Shimmie2, a booru without any official API.
    * This scrapper is should work with any booru website running shimmie2,
    * Tested on shimmie2 test instance and https://booru.soy
    * 
    * @class
    * @classdesc shimmie2-js is a library that allows you to scrape data from Shimmie2 booru software.
    */
class Shimmie2js {
    /**
        * Creates a new instance of the Shimmie2js class.
        * 
        * @param {string} instance - The URL of the Shimmie2 API instance. (ex: https://booru.soy)
        */
    constructor(instance) {
        this.instance = instance
    }

    /**
        * This method will return one (1) random post based on the tags you provided.
        * 
        * @param {Array<string>} tags - An array of tags.
        * @returns {Promise<{status: "ok", code: HTTPStatusCode, data: { id: number, link: url, mime: string, thumbnail: url, image: url, tags: Array<string> } } | { status: 'error', code: HTTPStatusCode }>} The last list of items or an error object.
        * @throws {Error} If the tags parameter is not an array.
        * @throws {HTTPStatusCode} HTTP Status code != 200
        */
    async search(tags) {
        if (!Array.isArray(tags)) {
            throw new Error('Tags must be an array.')
        }

        // Gets the first page of the tag array
        let firstPage = `${this.instance}/post/list/${tags.length > 1 ? tags.join('%20') : tags[0]}/1`
        let lastPage
        let type

        // We need to know how many list pages there are, or if there's only one post
        try {
            let response = await axios.get(firstPage)
            let $ = cheerio.load(response.data)
        
            // If the url is diff than firstPage it means that there is only one image on the tags at it will send you to the post 
            if (response.request.res.responseUrl !== firstPage) {
                lastPage = 1
                type = 'post'

            // Checks the first page for the maximum number of pages, if there is no 'Last' button then you know there is only 1 list page
            } else if ($('a:contains("Last")').length === 0) {
                lastPage = 1
                type = 'list'

            // Gets last page by looking at the navigation on the list
            } else {
                lastPage = Number($('a:contains("Last")').attr('href').split('/').pop())
                type = 'list'

            }

        } catch (error) {
            throw new Error(error.response.status)
        }

        // Generate a random number from 1 to lastPage
        let randomPage = Math.floor(Math.random() * lastPage + 1)
        let randomPageUrl = `${this.instance}/post/list/${tags.length > 1 ? tags.join('%20') : tags[0]}/${randomPage}`

        try {
            let response = await axios.get(randomPageUrl)
            let $ = cheerio.load(response.data)

            if (type === 'list') {
                return {
                    status: 'ok',
                    code: response.status,
                    data: sectionGetPost($, this.instance)
                }
            } else {
                return {
                    status: 'ok',
                    code: response.status,
                    data: termGetPost($, this.instance, response) // response is used to get the actual url of the post page because you are redirected to it
                }
            }
        } catch (error) {
            throw new Error(error.response.status)
        }
    }
}

function sectionGetPost($, host) {
    // This is the html element where posts are displayed in a Shimmie2 results page
    let posts = $('a.thumb.shm-thumb.shm-thumb-link')

    let result = []

    posts.each((index, element) => {
        let link = new URL($(element).attr('href'), host).href
        // Images can be loaded with nothing but their hash which makes it way easier to get their links
        let thumbnail = `${host}/_thumbs/${$(element).find('img').attr('src').split('/_thumbs/')[1].split('/')[0]}`
        let image = `${host}/_images/${$(element).find('img').attr('src').split('/_thumbs/')[1].split('/')[0]}`
        let tags = $(element).attr('data-tags').split(' ')
        let id = Number(link.split('/')[link.split('/').length - 1])
        let mime = $(element).attr('data-mime')

        result.push({
            id: id,
            link: link,
            mime: mime,
            thumbnail: thumbnail,
            image: image,
            tags: tags
        })
    })

    // Now we generate a random number based on the number of posts on the list and return it
    let random = Math.floor(Math.random() * result.length)

    return result[random]
}

function termGetPost($, host, response) {
    let post = $('#main_image')

    // On post pages the url of the image is inside <source> if it's a video
    if (post.is('video')) {
        post = $('video#main_image source')
    } else {
        post = $('img#main_image')
    }

    // Could have used the opengraph og:url on the meta tag but that would be retarded and ugly
    let link = response.request.res.responseUrl
    let thumbnail = `${host}/_thumbs/${post.attr('src').split('/_images/')[1].split("/")[0]}`
    let image = `${host}/_images/${post.attr('src').split('/_images/')[1].split("/")[0]}`
    let tags = $('meta[name="keywords"]').attr('content').split(', ')
    let id = Number(link.split('/')[link.split('/').length - 1])
    let mime = $('img#main_image').length === 1 ? $('img#main_image').attr('data-mime') : $('video source').attr('type')

    return {
        id: id,
        link: link,
        mime: mime,
        thumbnail: thumbnail,
        image: image,
        tags: tags
    }
}

module.exports = Shimmie2js
          |:.:.:.:.:.:.:.:.:/.:.:.:.:.:./.:.:.:.:.:.:.:.:.:.:.:.:.:./|   '. :.:.:.:.:.:.:.:.:.:.l.ヽ:.:.:.:.l:.:.:.:.:.:.:.:.:.:∧\:.:.:.',
          |:.:.:.:.:.:.:./.:.:.:.:.:./.:.:.:.:.:.:.:.:.:.:.:.: , ′   l :.:.:.:.:.:.:.:.:.:| ∨.:.:.l:.:.:.:.:.:.:.:.:.:.∧ \:.'.
          |:.:.:.:.:.:/.:.:.:.:.: /.:.:.:.:.:.:.:.:. ヽ/ /    |:.:.:.:.:.:.:./:.:.|   y'.:.:|:.:.:.:.|:.:.:.:.:.:.∧   ヘ.
          |:.:.:.:./.:.:.:.//.:.:.:.:.:.:.:.:.:.:/ \,′   !:.:.:.:.:.:/|:.:./,/ '.:.:| :.:.:.|:.:.:.:.:.:.:.:.|
          |:.:.:/.:.:./.:./.:.:.:.:.:.:.:.:./   /` ‐-‐'|:.:.:./ |ァ'´    |:.:| :.:.:.|:.:.:.:.:.:.:.:.|
          |:.:.i.:.:./.:.:.: '.:.:.:.:.:.:.:/   ,/       |:.:.:/   ′    }.:.| :.:.:.|:.:.:.| :.:.:.:|
          |:.:.|:./.: rヘ|:.:.:.:.:.:/:.| 三三三三三  |:./   三三三 ハ|:.:.:.:.|:.:.:.| :.:.:.:|
          |:.:.l〃.:{  |:.:.:.:./.:.:.|             l.'         .i.:.:.i.:.:.:∧ :.:|ヽ :.:|
          |:.:./.:.:.:.\|:.:.:/.:.:.:.:|                '       |:.:.||:.:/  :.:.| '.:.:l
          |:./.:.:.:.:.:.:. !:./.:.:.:.:.:.'、            ,.ー--、    }.:.:|レ′ ∨  V
          |/.:.:.:.:.:.:.:.:V '´  ̄`ヽ.、            ´`¨¨{. |_.  '.:.:.:.|
         /.:.:.:.:.:.:.:./         ヘ `    . __. -r1 |:.:.:.:.:.:.:.:.|
         :.:.:.:.:.:.:, '         '. \    | _.   -┴ー──┴┐
         :.:.:.:.:./           |  \  r' |   -──────i'
    

< KONAKONA.MOE >

contact - projects - blog - log

counter

counter