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 }