Contenu | Rechercher | Menus

Annonce

Si vous rencontrez des soucis à rester connecté sur le forum (ou si vous avez perdu votre mot de passe) déconnectez-vous et reconnectez-vous depuis cette page, en cochant la case "Me connecter automatiquement lors de mes prochaines visites".
Test de l'ISO d'Ubuntu francophone : nous avons besoin de testeurs pour la version francophone d'Ubuntu 14.04. Liens et informations ici.

#151 Le 17/06/2012, à 07:37

Ras'

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

\o/


Va t'faire shampouiner en GMT-4 !
http://blag.xserver-x.org/
 
Les types awesome n'ont rien à prouver. À personne.

Hors ligne

#152 Le 17/06/2012, à 07:45

souen

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

rrrrrogntudjû................M'enfin!


Tu es programmé, mais tu es libre.
Ubuntu Studio
"Si il n'y a pas de solution, c'est qu'il n'y a pas problème !"
(sagesse Shadok)

Hors ligne

#153 Le 17/06/2012, à 07:56

ljere

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

alors voici ou en est le script

#!/usr/bin/python
# encoding: utf-8 

from BeautifulSoup import BeautifulSoup
import urllib2
import re
import time
import sys
import mechanize

ignoreList = (
    'compteur des leve tot',
    )

class Day:
    """un jours dure de 5h à 9h du matin exclu ([5h:9h[) il contient la dernière entrée (points) de ce jour pour chaques joueurs"""
    def __init__(self):
        self.entries={}

    def __str__(self):
        for entry in self.entries.items():
            print entry,'+',entries[entry]

def utcFrance():
    return 1 + time.localtime(time.time())[-1] #1 + 1 si on est a l'heure d'été


class Date:
    def __init__(self,tuple=(5,8),utc=utcFrance()):
        self.h = (int(tuple[0])-utcFrance()+24+utc)%24
        self.m = int(tuple[1])

    def __cmp__(self, other):
        return cmp(self.points(),other.points())

    def points(self):
        pts =  {5: 10, 6: 6, 7: 3, 8: 1}
        return pts.get(self.h, 0) 

class Entry:
    def __init__(self,name='',date=Date(),edit=Date()):
        self.name = name
        self.date = max(date,edit)

    def setName(self, name):
        self.name = name

    def setDate(self, date):
        if date.points()>self.date.points(): self.date = date

class Score:
    def __init__(self, tuple):
        self.name = tuple[1]
        self.num = int(tuple[0])

    def __gt__(self, other):
        return self.num>other.num

    def __str__(self):
        return '%i    %s' %(self.num, self.name)

def getPage(url, entries, stat, urlscore):
    # essaye jusqu'a 15 fois de récupérer la page
    for i in range(15):
        try:
            page = BeautifulSoup(urllib2.urlopen(url))
            break
        except:
            print 'essai: %s' % i
            if i==14: raise
            time.sleep(60)

    print 'page récupéré, travail en cours'
    for post in page.findAll("div","blockpost rowodd blockpost1")+page.findAll("div","blockpost roweven")+page.findAll("div","blockpost rowodd"):
        str_date = str(post.find("h2").find("span").find("a")).split('>')[1]
        if (
           (str_date.split(' ')[0] in ["Aujourd\'hui"]
             and int(str_date.split(' ')[2].split('<')[0].split(':')[0]) in range(5,8))
           ):
            urlscore = url
            try:
                entry = Entry(str(post.find("div","postleft").find("a")).split(">")[1].split("<")[0])
            except:
                entry = Entry(str(post.find("div","postleft").find("strong")).split(">")[1].split("<")[0])

            utc=utcFrance()
            #print entry.name
            if 'GMT' in str(post):
                try:
                    #print str(post).split("GMT")[-1]
                    utc = int(str(post).split("GMT")[-1].split(" ")[0].split("<")[0])
                    print 'GMT found',utc
                except:
                    print "no good GMT!"
            entry.setDate(Date(str_date.split(" ")[2].split("<")[0].split(":"),utc))
            if str_date.split(" ")[2].split("<")[0].split(":")[0] not in stat:
                stat[str_date.split(" ")[2].split("<")[0].split(":")[0]] = 1
            else:
                stat[str_date.split(" ")[2].split("<")[0].split(":")[0]] += 1

            if post.find('p','postedit') is not None:
                print "edit found"
                try:
                    entry.setDate(Date(str(post.find("p","postedit").find("em")).split("à")[1][1:6].split(":"),utc))
                except:
                    print 'error ->'+str(str(post.find("p","postedit").find("em")).split("à"))
            #on ne veut pas les points des pseudos dans ignoreList
            if entry.name not in ignoreList and (entry.date.points() is not 0): entries.addEntry(entry)


    # erreur si le topic fait moins d'une page de long,FIXME!
    #result = int(url.split('p=')[1]) < int(str(page.findAll('a')[-7]).split('p=')[1].split('"')[0]) #en espérant que le nombre de liens en bas de page ne change pas! :/
    if str(page.find('p','pagelink conl')).split('conl">')[1].split('</p')[0].split(str(page.find('p','pagelink conl').find('strong'))) == ['', '']:
        result = False
    else:
        try:
            result = int(url.split('p=')[1]) < int(str(page.find('p','pagelink conl').findAll('a')[-2]).split('p=')[1].split('"')[0])
        except IndexError:
            result = False
    #FIXED!
    if not result:
        # on vérifie si le sujet est fermé, auquel cas on renvoie le dernier lien fourni sur la page
        if "Discussion fermée" in ''.join( (str( i) for i in page.findAll('p','postlink conr'))):
            print str(page.findAll('div','postmsg')[-1])
            result = str(page.findAll('div','postmsg')[-1].findAll('a')[-1]).split('"')[1]
    return result, urlscore

def post():
    file = open(".compteur_logins","r")
    login = file.readline().split('\n')[0]
    password = file.readline().split('\n')[0]
    file.close()

    cookieJar = mechanize.CookieJar()

    opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookieJar))
    opener.addheaders = [("User-agent","Mozilla/5.0 (compatible)")]
    mechanize.install_opener(opener)
    fp = mechanize.urlopen("http://forum.ubuntu-fr.org/login.php")
    forms = mechanize.ParseResponse(fp)
    fp.close()
    import pprint
    pprint.pprint(forms)
    form = forms[1]
    form["req_username"] = login
    form["req_password"] = password
    fp = mechanize.urlopen(form.click())
    print fp.read()
    fp.close()

    file = open('url','r')
    tid = file.readline().split('=')[1] # la premiere ligne contenant l'addresse du topic.
    file.close()
    fp = mechanize.urlopen("http://forum.ubuntu-fr.org/post.php?tid="+tid)
    forms = mechanize.ParseResponse(fp)
    fp.close()

    print "http://forum.ubuntu-fr.org/post.php?tid="+tid
    print forms[0]
    form = forms[1]
    title = ((_file == "count") and "Scores totaux, depuis le début")
    form["req_message"]  = title+" :[cøde]"+'\xe2\x80\xad'
    file = open(_file, 'r')
    scores=file.readlines()
    stats = {}
    for i in range(len(scores)):#on veut toutes les lignes restantes
        if i == 0:
            tmpRange = 0
        elif scores[i].split(" ")[0] == scores[i-1].split(" ")[0]:
            pass
        else:
            tmpRange = i
        #et on ajoute la ligne avec le bon rang a l'entrée
        form["req_message"] += (
            (('\xe2\x80\xae' in scores[i-1] and '\xe2\x80\xac') or '')+
            (("Quelqu'un" in scores[i] and "*** champion des leves tot ***\n") or '')+
            str(tmpRange+1)+
            ") "+scores[i]+
            (("Quelqu'un" in scores[i]
                and "******************************************************************************\n") or '')
        )
    form["req_message"] += "[/cøde]"

    fp = mechanize.urlopen(form.click())
    fp.close()

def main(urlfile, files):
    debug = False
    stats = {}
    f=open(urlfile,"r")
    url=urlscore=f.readline().split('\n')[0]
    f.close()
    entries = Day()

    while True:
        print "lecture de la page "+url
        res = getPage(url, entries, stats, urlscore)
        urlscore = res[1]
        if not res[0]: break
        url=url.split('p=')[0]+'p='+str(1+int(url.split('p=')[1]))
        if res[0] is not True:
            url = url.split('?')[0]+'?'+res[0].split('?')[1]+'&p=1'

    if not debug:
        f=open(urlfile,"w")
        f.write(urlscore+'\n')
        f.close()


        new_scores=[]
        for entry,num in entries.entries.items():
            for score in scores:
                if entry.lower() == score.name.lower():
                    score.num+=num
                    break
                if score is scores[-1]:
                    new_scores.append(Score([num,entry]))
                    break
        scores+=new_scores

        # vérification des doublons
        for nScore in range(len(scores)-1):
            for mScore in range(nScore+1,len(scores)-1):
                try:
                    if scores[nScore].name.lower() == scores[mScore].name.lower():
                        scores[nScore].num+=scores[mScore].num
                        del(scores[mScore])
                except:
                    pass
        scores.sort(reverse=True)
        if not debug:
            for score in scores: print score
            f=open(file, "w")
            for score in scores: #print score
                f.write('%s\n'%score)
            f.close()

            for i in range(15):
                try:
                    post(file, stats)
                    stats = {}
                    break
                except:
                    if i == 14: raise
                    time.sleep(60)
        time.sleep(10)

main("url",["count",])
#post("count",{})

donc j'en suis à cette parti et je suis à peu prés sur que quelquechose cloche

    # erreur si le topic fait moins d'une page de long,FIXME!
    #result = int(url.split('p=')[1]) < int(str(page.findAll('a')[-7]).split('p=')[1].split('"')[0]) #en espérant que le nombre de liens en bas de page ne change pas! :/
    if str(page.find('p','pagelink conl')).split('conl">')[1].split('</p')[0].split(str(page.find('p','pagelink conl').find('strong'))) == ['', '']:
        result = False
    else:
        try:
            result = int(url.split('p=')[1]) < int(str(page.find('p','pagelink conl').findAll('a')[-2]).split('p=')[1].split('"')[0])
        except IndexError:
            result = False
    #FIXED!
    if not result:
        # on vérifie si le sujet est fermé, auquel cas on renvoie le dernier lien fourni sur la page
        if "Discussion fermée" in ''.join( (str( i) for i in page.findAll('p','postlink conr'))):
            print str(page.findAll('div','postmsg')[-1])
            result = str(page.findAll('div','postmsg')[-1].findAll('a')[-1]).split('"')[1]
    return result, urlscore

Modérateur d'ubuntu-fr.org
athlon 2800+, nvidia FX5200 et 2 Go de ram et sempron 3000+, ati radeon et 1 Go de ram sur voyager 12.04 32 bit
Toshiba satellite_c670d-11l sur openbox/xubuntu 14.04 64 bit
Mon Blog et Une découverte

Hors ligne

#154 Le 17/06/2012, à 08:07

souen

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

NON AUX CADENCES INFERNALES mad


Tu es programmé, mais tu es libre.
Ubuntu Studio
"Si il n'y a pas de solution, c'est qu'il n'y a pas problème !"
(sagesse Shadok)

Hors ligne

#155 Le 17/06/2012, à 08:32

ljere

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

en effet je vais trop vite donc j'en suis la

        if (
           (str_date.split(' ')[0] in ["Aujourd\'hui"]
             and int(str_date.split(' ')[2].split(':')[0]) in range(5,8))
           ):
            urlscore = url
            try:
                entry = Entry(str(post.find("div","postleft").find("a")).split(">")[1].split("<")[0])
            except:
                entry = Entry(str(post.find("div","postleft").find("strong")).split(">")[1].split("<")[0])

si quelqu'un a le temps de regarder (je n'oblige personne biensur) est ce que ces ".split("<")[0]" sont à supprimer ou à garder


Modérateur d'ubuntu-fr.org
athlon 2800+, nvidia FX5200 et 2 Go de ram et sempron 3000+, ati radeon et 1 Go de ram sur voyager 12.04 32 bit
Toshiba satellite_c670d-11l sur openbox/xubuntu 14.04 64 bit
Mon Blog et Une découverte

Hors ligne

#156 Le 17/06/2012, à 08:36

PPdM

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

Salut

Sören Kierkegaard  a écrit :

La vie ne se comprend que par un retour en arrière, mais on ne la vit qu’en avant.

Hors ligne

#157 Le 17/06/2012, à 09:09

raspouillas

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

@ljere contacter @cep.

#158 Le 17/06/2012, à 09:30

Mindiell

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

ljere a écrit :

en effet je vais trop vite donc j'en suis la

        if (
           (str_date.split(' ')[0] in ["Aujourd\'hui"]
             and int(str_date.split(' ')[2].split(':')[0]) in range(5,8))
           ):
            urlscore = url
            try:
                entry = Entry(str(post.find("div","postleft").find("a")).split(">")[1].split("<")[0])
            except:
                entry = Entry(str(post.find("div","postleft").find("strong")).split(">")[1].split("<")[0])

si quelqu'un a le temps de regarder (je n'oblige personne biensur) est ce que ces ".split("<")[0]" sont à supprimer ou à garder

Oui, ils sont importants là.
En fait, le code source contient ça :

                <div class="postleft">
                    <dl>
                        <dt><strong><a href="profile.php?id=40383">Ras&amp;#039;</a></strong></dt>
                                                <dd class="postavatar"><img src="http://forum.ubuntu-fr.org/img/avatars/40383.png?m=1310129728" width="60" height="60" alt="" /></dd>
                    </dl>
                </div>

Tu cherches donc un div du nom de postleft, au sein duquel tu cherches une ancre a donc ça :

<a href="profile.php?id=40383">Ras&amp;#039;</a>

En "splittant" sur > et en prenant l'élément 1 (le deuxième donc), tu obtiens ça :

Ras&amp;#039;</a

Puis un dernier split sur < en prenant le premier élément :

Ras&amp;#039;

L'exception a lieu lorsque le posteur (ici Ras) n'a pas d'ancre (pas de profil ?). Tu passes alors par le tag strong qui est forcément  présent !

Et voilà ! Tu peux continuer...

PS: Ah bonjour aussi, et le samedi ma philosophie c'est : pas de point smile

Hors ligne

#159 Le 17/06/2012, à 09:49

ljere

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

merci pour tes explications super clair
je croyais qu'on était dimanche moi


Modérateur d'ubuntu-fr.org
athlon 2800+, nvidia FX5200 et 2 Go de ram et sempron 3000+, ati radeon et 1 Go de ram sur voyager 12.04 32 bit
Toshiba satellite_c670d-11l sur openbox/xubuntu 14.04 64 bit
Mon Blog et Une découverte

Hors ligne

#160 Le 17/06/2012, à 18:17

golgoth42

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

@ljere : le truc que je t'ai passé à été utile ou pas ?


Il n'est jamais trop tard pour apprendre, avec de l'aide et de la volonté. (enfin, je l'espère!!)
http://pix.tdct.org http://paste.tdct.org

il est certaines envies que l'ont refoule longtemps… que faire quand elles reviennent très fort en pleine tronche…

Hors ligne

#161 Le 17/06/2012, à 19:00

ljere

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

pas réellement j'avais déjà récupéré cette version et déjà modifié plus profondément le code (par contre je suis surpris qu'il a fonctionné comme ça)
pour le compte de ton bot je n'en ai pas besoin j'avais déjà recréé un compte
mais je tiens quand même à te remercié wink
@raspouillas pourquoi devrais je contacter cep?


Modérateur d'ubuntu-fr.org
athlon 2800+, nvidia FX5200 et 2 Go de ram et sempron 3000+, ati radeon et 1 Go de ram sur voyager 12.04 32 bit
Toshiba satellite_c670d-11l sur openbox/xubuntu 14.04 64 bit
Mon Blog et Une découverte

Hors ligne

#162 Le 17/06/2012, à 19:08

Floyd Pepper

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

1    1790    FloydPepper
2    1544    pierguiard
3    1463    MdMax
4    1247    Azurea
5    1202    souen
6    971    Ras&#039;
7    777    raspouillas
8    552    Arcans
9    428    peterp@n
10    359    golgoth42
11    293    mindiell
12    277    omc
13    219    Πυλάδης
14    176    pololasi
15    117    edge_one
16    101    nathéo
17    99    karameloneboudeplus
18    61    agarwood
19    60    Niltugor
20    58    ljere
21    52    1101011
21    52    jeyenkil
23    43    Crocoii
24    42    nakraïou
24    42    DaveNull
26    40    Biaise
27    39    Clem_ufo
28    38    Atem18
29    22    marinmarais
30    18    Ju
31    13    Le grand rohr sha
32    10    Phoenix
32    10    FLOZz
32    10    sakul
32    10    SopolesRâ
36    6    wiscot
36    6    timsy
36    6    Slystone
36    6    Hibou57
36    6    tshirtman
36    6    marting
36    6    c4nuser
43    4    Morgiver
43    4    :!pakman
45    3    Phoenamandre
45    3    gonzolero
45    3    helly
45    3    Le Rouge
49    1    herewegoagain
49    1    TheUploader
49    1    Kyansaa
49    1    Xiti29

Vieux papy triste et sexiste, en attente d'être complètement con×. J'aurais tendance à ne pas utiliser de smilleys.
Le plus tu t'fais chier, le plus t'es emmerdé.

Hors ligne

#163 Le 17/06/2012, à 21:10

Mindiell

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

ljere a écrit :

je croyais qu'on était dimanche moi

Comme quoi, j'ai du dormir trop longtemps mad

Hors ligne

#164 Le 17/06/2012, à 22:25

souen

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

Pour un topic des lève-tôt je trouve que les points devraient s'arrêter dès 7h, max 7.30.
Après 7h 30 je trouve que l'on ne peut plus se considérer comme étant des lève-tôt.


Tu es programmé, mais tu es libre.
Ubuntu Studio
"Si il n'y a pas de solution, c'est qu'il n'y a pas problème !"
(sagesse Shadok)

Hors ligne

#165 Le 18/06/2012, à 04:32

raspouillas

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

@ljere à écrit:
@raspouillas pourquoi devrais je contacter cep?

Parce que @cep ma donné des coups de main (ex:sur Debian Sid) et autres.
@cep est un excellent dev ...

#166 Le 18/06/2012, à 05:00

Ras&#039;

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

Ha !


Va t'faire shampouiner en GMT-4 !
http://blag.xserver-x.org/
 
Les types awesome n'ont rien à prouver. À personne.

Hors ligne

#167 Le 18/06/2012, à 06:48

Floyd Pepper

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

Ho !


Vieux papy triste et sexiste, en attente d'être complètement con×. J'aurais tendance à ne pas utiliser de smilleys.
Le plus tu t'fais chier, le plus t'es emmerdé.

Hors ligne

#168 Le 18/06/2012, à 07:20

souen

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

Hé !


Tu es programmé, mais tu es libre.
Ubuntu Studio
"Si il n'y a pas de solution, c'est qu'il n'y a pas problème !"
(sagesse Shadok)

Hors ligne

#169 Le 18/06/2012, à 07:21

ljere

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

grasse mat' du lundi


Modérateur d'ubuntu-fr.org
athlon 2800+, nvidia FX5200 et 2 Go de ram et sempron 3000+, ati radeon et 1 Go de ram sur voyager 12.04 32 bit
Toshiba satellite_c670d-11l sur openbox/xubuntu 14.04 64 bit
Mon Blog et Une découverte

Hors ligne

#170 Le 18/06/2012, à 08:05

raspouillas

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

bonjour ...

#171 Le 18/06/2012, à 16:44

Mindiell

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

Ah, tiens, me suis oublié là... smile

Hors ligne

#172 Le 18/06/2012, à 19:36

Floyd Pepper

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

1    1796    FloydPepper
2    1544    pierguiard
3    1463    MdMax
4    1247    Azurea
5    1205    souen
6    981    Ras&#039;
7    778    raspouillas
8    552    Arcans
9    428    peterp@n
10    359    golgoth42
11    293    mindiell
12    277    omc
13    219    Πυλάδης
14    176    pololasi
15    117    edge_one
16    101    nathéo
17    99    karameloneboudeplus
18    61    agarwood
18    61    ljere
20    60    Niltugor
21    52    1101011
21    52    jeyenkil
23    43    Crocoii
24    42    nakraïou
24    42    DaveNull
26    40    Biaise
27    39    Clem_ufo
28    38    Atem18
29    22    marinmarais
30    18    Ju
31    13    Le grand rohr sha
32    10    Phoenix
32    10    FLOZz
32    10    sakul
32    10    SopolesRâ
36    6    wiscot
36    6    timsy
36    6    Slystone
36    6    Hibou57
36    6    tshirtman
36    6    marting
36    6    c4nuser
43    4    Morgiver
43    4    :!pakman
45    3    Phoenamandre
45    3    gonzolero
45    3    helly
45    3    Le Rouge
49    1    herewegoagain
49    1    TheUploader
49    1    Kyansaa
49    1    Xiti29

Vieux papy triste et sexiste, en attente d'être complètement con×. J'aurais tendance à ne pas utiliser de smilleys.
Le plus tu t'fais chier, le plus t'es emmerdé.

Hors ligne

#173 Le 18/06/2012, à 20:59

ljere

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

/me est déjà 18ème cool


Modérateur d'ubuntu-fr.org
athlon 2800+, nvidia FX5200 et 2 Go de ram et sempron 3000+, ati radeon et 1 Go de ram sur voyager 12.04 32 bit
Toshiba satellite_c670d-11l sur openbox/xubuntu 14.04 64 bit
Mon Blog et Une découverte

Hors ligne

#174 Le 19/06/2012, à 03:35

golgoth42

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

souen a écrit :

Pour un topic des lève-tôt je trouve que les points devraient s'arrêter dès 7h, max 7.30.
Après 7h 30 je trouve que l'on ne peut plus se considérer comme étant des lève-tôt.

bah, à la base, c'est un fork du TdCT  ce topic, donc se lever tot c'est à relativiser par rapport aux 10 points couche-tard qui sont à partie de 3h du mat'

@ljere: désolé que ça t'ao pas été très utile, je suis pas trop ce topic depuis trop longtemps apparremment hmm


Il n'est jamais trop tard pour apprendre, avec de l'aide et de la volonté. (enfin, je l'espère!!)
http://pix.tdct.org http://paste.tdct.org

il est certaines envies que l'ont refoule longtemps… que faire quand elles reviennent très fort en pleine tronche…

Hors ligne

#175 Le 19/06/2012, à 06:17

PPdM

Re : Topic des lève-tôt… Faisons manger leurs caleçons aux couche-tard! [4]

Salut,

Henri Jeanson a écrit :

Le travail est un trésor. Le travail des autres, cela va de soi.

Hors ligne

Haut de page ↑