morpheus

A Matrix client written in Go-QT
git clone git://archive.git.mtrnord.blog/Nordgedanken/morpheus.git
Log | Files | Refs | README | LICENSE

user.go (5111B)


      1 package matrix
      2 
      3 import (
      4 	"bytes"
      5 	"image"
      6 	"image/color"
      7 	"image/draw"
      8 	// image/gif needed to load gif images
      9 	_ "image/gif"
     10 	// image/jpeg needed to load jpeg images
     11 	_ "image/jpeg"
     12 	"image/png"
     13 	// image/png needed to load png images
     14 	_ "image/png"
     15 	"strconv"
     16 	"strings"
     17 	"unicode"
     18 	"unicode/utf8"
     19 
     20 	log "github.com/sirupsen/logrus"
     21 
     22 	"github.com/Nordgedanken/Morpheus/matrix/db"
     23 	"github.com/disintegration/letteravatar"
     24 	"github.com/matrix-org/gomatrix"
     25 	"github.com/therecipe/qt/gui"
     26 
     27 	"github.com/dgraph-io/badger"
     28 	// golang.org/x/image/webp needed to load webp images
     29 	_ "golang.org/x/image/webp"
     30 
     31 	// golang.org/x/image/bmp needed to load bmp images
     32 	_ "golang.org/x/image/bmp"
     33 
     34 	// golang.org/x/image/riff needed to load riff images
     35 	_ "golang.org/x/image/riff"
     36 
     37 	// golang.org/x/image/tiff needed to load tiff images
     38 	_ "golang.org/x/image/tiff"
     39 )
     40 
     41 type circle struct {
     42 	p image.Point
     43 	r int
     44 }
     45 
     46 func (c *circle) ColorModel() color.Model {
     47 	return color.AlphaModel
     48 }
     49 
     50 func (c *circle) Bounds() image.Rectangle {
     51 	return image.Rect(c.p.X-c.r, c.p.Y-c.r, c.p.X+c.r, c.p.Y+c.r)
     52 }
     53 
     54 func (c *circle) At(x, y int) color.Color {
     55 	xx, yy, rr := float64(x-c.p.X)+0.5, float64(y-c.p.Y)+0.5, float64(c.r)
     56 	if xx*xx+yy*yy < rr*rr {
     57 		return color.Alpha{A: 255}
     58 	}
     59 	return color.Alpha{A: 0}
     60 }
     61 
     62 func generateGenericImages(displayname string, size int) (imgData []byte, err error) {
     63 	identifier := displayname
     64 	if (identifier[0] == '#' || identifier[0] == '!' || identifier[0] == '@') && len(identifier) > 1 {
     65 		identifier = identifier[1:]
     66 	}
     67 
     68 	avatarChar, _ := utf8.DecodeRuneInString(identifier)
     69 	img, LetterAvatarErr := letteravatar.Draw(size, unicode.ToUpper(avatarChar), nil)
     70 
     71 	if LetterAvatarErr != nil {
     72 		err = LetterAvatarErr
     73 		return
     74 	}
     75 
     76 	buf := new(bytes.Buffer)
     77 	EncodeErr := png.Encode(buf, img)
     78 	if EncodeErr != nil {
     79 		err = EncodeErr
     80 		return
     81 	}
     82 	imgData = buf.Bytes()
     83 	return
     84 }
     85 
     86 // GetOwnUserAvatar returns a *gui.QPixmap of an UserAvatar
     87 func GetOwnUserAvatar(cli *gomatrix.Client) (avatar *gui.QPixmap, err error) {
     88 	avatar, err = GetUserAvatar(cli, cli.UserID, 61)
     89 	return
     90 }
     91 
     92 // GetUserAvatar returns a *gui.QPixmap of an UserAvatar
     93 func GetUserAvatar(cli *gomatrix.Client, mxid string, size int) (avatarResp *gui.QPixmap, err error) {
     94 	cacheDB, DBOpenErr := db.OpenCacheDB()
     95 	if DBOpenErr != nil {
     96 		log.Fatalln(DBOpenErr)
     97 	}
     98 
     99 	// Init local vars
    100 	var avatarData []byte
    101 	var IMGdata []byte
    102 
    103 	// Get cache
    104 	DBErr := cacheDB.View(func(txn *badger.Txn) error {
    105 		roomAvatarDataItem, QueryErr := txn.Get([]byte("user|" + mxid + "|avatarData" + strconv.Itoa(size) + "x" + strconv.Itoa(size)))
    106 		if QueryErr != nil && QueryErr != badger.ErrKeyNotFound {
    107 			return QueryErr
    108 		}
    109 		if QueryErr != badger.ErrKeyNotFound {
    110 			avatarDataBytes, avatarDataErr := roomAvatarDataItem.Value()
    111 			avatarData = avatarDataBytes
    112 			return avatarDataErr
    113 		}
    114 		return nil
    115 	})
    116 	if DBErr != nil {
    117 		err = DBErr
    118 		return
    119 	}
    120 
    121 	//If cache is empty do a ServerQuery
    122 	if len(avatarData) <= 0 {
    123 		// Get avatarURL
    124 		urlPath := cli.BuildURL("profile", mxid, "avatar_url")
    125 		s := struct {
    126 			AvatarURL string `json:"avatar_url"`
    127 		}{}
    128 
    129 		_, ReqErr := cli.MakeRequest("GET", urlPath, nil, &s)
    130 		if ReqErr != nil {
    131 			err = ReqErr
    132 			return
    133 		}
    134 		avatarURL := s.AvatarURL
    135 
    136 		// If avatarURL is not empty (aka. has a avatar set) download it at the size of 100x100. Else make the data string empty
    137 		if avatarURL != "" {
    138 			hsURL := cli.HomeserverURL.String()
    139 			avatarURLSplits := strings.Split(strings.Replace(avatarURL, "mxc://", "", -1), "/")
    140 
    141 			urlPath := hsURL + "/_matrix/media/r0/thumbnail/" + avatarURLSplits[0] + "/" + avatarURLSplits[1] + "?width=" + strconv.Itoa(size) + "&height=" + strconv.Itoa(size) + "&method=crop"
    142 
    143 			data, ReqErr := cli.MakeRequest("GET", urlPath, nil, nil)
    144 			if ReqErr != nil {
    145 				err = ReqErr
    146 				return
    147 			}
    148 			IMGdata = data
    149 		} else {
    150 			DisplayNameResp, _ := cli.GetDisplayName(mxid)
    151 			DisplayName := DisplayNameResp.DisplayName
    152 			var GenerateImgErr error
    153 			IMGdata, GenerateImgErr = generateGenericImages(DisplayName, size)
    154 			if GenerateImgErr != nil {
    155 				err = GenerateImgErr
    156 				return
    157 			}
    158 		}
    159 
    160 		// Update cache
    161 		DBSetErr := cacheDB.Update(func(txn *badger.Txn) error {
    162 			DBSetErr := txn.Set([]byte("user|"+mxid+"|avatarData"+strconv.Itoa(size)+"x"+strconv.Itoa(size)), IMGdata)
    163 			return DBSetErr
    164 		})
    165 		if DBSetErr != nil {
    166 			err = DBSetErr
    167 			return
    168 		}
    169 	} else {
    170 		IMGdata = avatarData
    171 	}
    172 
    173 	r := bytes.NewReader(IMGdata)
    174 	srcIMG, _, DecodeErr := image.Decode(r)
    175 	if DecodeErr != nil {
    176 		err = DecodeErr
    177 	}
    178 
    179 	// Convert avatarimage to QPixmap for usage in QT
    180 	canvas := image.NewRGBA(srcIMG.Bounds())
    181 	cx := srcIMG.Bounds().Min.X + srcIMG.Bounds().Dx()/2
    182 	cy := srcIMG.Bounds().Min.Y + srcIMG.Bounds().Dy()/2
    183 	draw.DrawMask(canvas, canvas.Bounds(), srcIMG, image.ZP, &circle{image.Point{cx, cy}, cx}, image.ZP, draw.Over)
    184 
    185 	avatar := gui.NewQPixmap()
    186 	buf := new(bytes.Buffer)
    187 	ConvErr := png.Encode(buf, canvas)
    188 	if ConvErr != nil {
    189 		err = ConvErr
    190 	}
    191 
    192 	str := buf.Bytes()
    193 
    194 	avatar.LoadFromData(string(str[:]), uint(len(str)), "PNG", 0)
    195 	avatarResp = avatar
    196 	return
    197 }